0.0.0-master.ede0727 • Published 6 years ago

graphql-santa v0.0.0-master.ede0727

Weekly downloads
5
License
MIT
Repository
github
Last release
6 years ago
Please beware that this is a PROTOTYPE. Do NOT use this for serious work. Thanks! 🎄

graphql-santa

Introduction

graphql-santa is a GraphQL API framework. It takes a code-first approach (as opposed to schema-first) and brings together a set of tools that provide robust type safety so that if your app compiles, you have a much higher degree of confidence than with vanilla JavaScript or just TypeScript.

graphql-santa brings Nexus, Prisma, Apollo Server and more together into a pluggable system (in fact Prisma features are implemented as a plugin).

Get started

  1. For this tutorial we will use postgres. Install it if needed and then get its connection URL. Check out our db setup guide if unsure.

  2. Kick off a new project. Say yes (y) to the prisma option. Choose PostgreSQL for the db option. Take a few moments to look around, poke things. But don't feel pressure to understand everything right away : )

    npx graphql-santa

Get a sense for db-to-api workflow

Our Hello World schema doesn't account for information about moons, lets change that.

  1. Start by updating our data layer to model information about moons. We don't want to go crazy scientific here but a bit of modelling will serve us well. A world may have more than one moon, and a moon may have properites in its own right. So lets give moons a first class model representation. Then, we can connect them to their respective worlds:

      model World {
        id         Int    @id
        name       String @unique
        population Float
    +   moons      Moon[]
      }
    
    + model Moon {
    +   id    Int    @id
    +   name  String
    +   world World
    + }

    graphql-santa reacts to changes in your Prisma schema. By saving the above, your dev database will be automatically migrated and photon regenerated. You literally now just move on to updating your GraphQL API.

  2. We have data about Earth from before, but now we need to update it with information about its moon. Instead of working with photon inside one-off scripts, lets enhance our API and make the update as if a client app were.

    We're going to need to expose the moons world field to clients

      app.objectType({
        name: "World",
        definition(t) {
          t.model.id()
          t.model.name()
          t.model.population()
    +     t.model.moons()
        }
      })

    Upon doing this however, we will see a warning in our dev mode logs:

     Warning: Your GraphQL `World` object definition is projecting a field `moons` with `Moon` as output type, but `Moon` is not defined in your GraphQL Schema
     Warning: in /Users/jasonkuhrt/foobar/src/schema.ts:10:13
    
       6 definition(t) {
       7 t.model.id();
       8 t.model.name();
       9 t.model.population();
     → 10 t.model.moons();

    The feedback is pretty clear already but to restate: The problem is that we're project a Prisma model field (moons) that is a connection to another Prisma model (Moon) that has not been projected on our API layer. So let's do that now:

    +app.objectType({
    +  name:'Moon',
    +  definition(t){
    +    t.model.id()
    +    t.model.name()
    +    t.model.world()
    +   }
    +})

    Do not copy-paste. Instead type this out yourself and take note how autcompletion within the definition block on t.model effectively guides you to success.

    Once you have projected Moon from your data layer to your API layer, you will see that the dev mode warning and TypeScript error are now resolved. 🙌

    If you go to your GraphQL Playground now you will see that your GraphQL schema now contains your Moon data shape too. But of course we still need to update Earth with data about its moon. To achieve that we're going to expose CRUD actions that clients can use to update Earth.

    +app.mutationType({
    +  definition(t){
    +    t.crud.updateOneWorld()
    +  }
    +})

    Again do not copy-paste. Type this out and see how it feels. Notice how auto-completion guides you from start to finish.

    If we go back to our schema in GraphQL Playground now, we'll see a significant number of additions to the schema, a result of the CRUD features we've just enabled.

    Now, let's give Earth its moon!

    mutation addMoonToEarth {
      updateOneWorld(
        where: { name: "Earth" }
        data: { moons: { create: { name: "moon" } } }
      ) {
        name
        moons {
          name
        }
      }
    }

    You should see a result like:

    {
      "data": {
        "updateOneWorld": {
          "name": "Earth",
          "moons": [
            {
              "name": "moon"
            }
          ]
        }
      }
    }
  3. Deploy to Heroku

    For this step, create an account at Heroku and setup the CLI.

    1. Create a new app: heroku apps:create
    2. Attach your project to the app: heroku git:remote --app <app-name>
    3. Add a postgres database to it: heroku addons:create heroku-postgresql --app <app-name>
    4. Get the postgres database credentials: heroku pg:credentials:url --app <app-name>
    5. Initialize the postgres database: npx graphql-santa db init --connection-url <connection-url>
    6. Deploy using the git push to master workflow. See your app running in the cloud!
  4. Conclusion

    Hopefully that gives you a taste of the power under your finger tips. There's a ton more to discover. Happy coding! 🙌

Guide

Adding Prisma

Overview

Prisma Framework is a next-generation developer-centric tool chain focused on making the data layer easy. In turn, graphql-santa makes it easy to integrate Prisma Framework into your app. You opt-in by creating a schema.prisma file somewhere in your project. Then, the following things automatically happen:

  1. graphql-santa CLI workflows are extended: db, dev, build
    1. On build, Prisma generators are run
    2. During dev, Prisma generators are run after prisma schema file changes
    3. db command becomes powered by prisma2 lift
  2. The nexus-prisma Nexus plugin is automatically used. This you get access to t.model and t.crud.
  3. An instance of the generated Photon.JS client is a added to context under photon property
  4. The TypeScript types representing your Prisma models are registered as a Nexus data source. In short this enables proper typing of parent parameters in your resolves. They reflect the data of the correspondingly named Prisma model.

Example

Install the prisma plugin:

npm install graphql-santa-plugin-prisma

Add a schema.prisma file and fill it out with some content

mkdir -p prisma
touch prisma/schema.prisma
// prisma/schema.prisma

datasource db {
  provider = "sqlite"
  url      = "file:dev.db"
}

model User {
  id   Int    @id
  name String
}

Enter dev mode:

npx graphql-santa dev

The following shows an example of transitioning your API codebase to use the extensions brought on by the Prisma extension.

Using the model DSL:

  objectType({
    name: 'User',
    definition(t) {
-     t.id('id)
-     t.string('name')
+     t.model.id()
+     t.model.name()
    },
  })

Using the photon instance on ctx:

  queryType({
    definition(t) {
      t.list.field('users', {
        type: 'User',
-       resolve() {
-         return [{ id: '1643', name: 'newton' }]
+       resolve(_root, _args, ctx) {
+         return ctx.photon.users.findMany()
        },
      })
    },
  })

Databases

Setup a local PostgreSQL

The reccommended way to run postgres locally is with docker, because it is easy flexible and reliable.

  1. Start a postgres server for your app:

    docker run --detach --publish 5432:5432 --name 'myapp-db' --env POSTGRES_PASSWORD=postgres postgres
  2. Now you can use a connection URL like:

    postgresql://postgres:postgres@localhost:5432/myapp

If you don't want to use a docker, here are some links to alternative approaches:

Going to Proudction

Once you're ready to go to production just build your app and run the start module with node.

$ npx graphql-santa build
$ node node_modules/.build

Heroku

  "scripts": {
    "build": "graphql-santa build",
    "start": "node node_modules/.build"
  }

Conventions

schema.ts | schema/*

Optional –– Your GraphQL type definitions.

About

It can be a single module or folder of modules. Multiple instances of module/folder-modules throughout your source tree is supported.

In dev mode schema modules are synchronously found and imported at server boot time. At build time however static imports for all schema modules are inlined for boot performance.

Aliases

n/a

app.ts

Optional –– The entrypoint to your app

About

There can only be at most a single app.ts/server.ts/service.ts module in your source tree.

This module is optional when you just have schema modules and so graphql-santa already knows how import them into the final build. Otherwise you'll need this module to import your custom modules etc.

Aliases
server.ts service.ts

Example Layouts

Nothing!

Nano

schema.ts

Micro

app.ts
schema.ts

Basic

app/
  server.ts
  schema.ts
prisma/
  schema.prisma

API

app

A singleton graphql-santa app. Use this to build up your GraphQL schema and configure your server.

Example

// schema.ts

import { app } from 'graphql-santa'

app.objectType({
  name: 'Foo',
  definition(t) {
    t.id('id')
  },
})

app.addToContext

Add context to your graphql resolver functions. The objects returned by your context contributor callbacks will be shallow-merged into ctx. The ctx type will also accurately reflect the types you return from callbacks passed to addToContext.

Example

// app.ts

import { app } from 'graphql-santa'

app.addToContext(req => {
  return {
    foo: 'bar',
  }
})

app.objectType({
  name: 'Foo',
  definition(t) {
    t.string('foo', (_parent, _args, ctx) => ctx.foo)
  },
})

app.<nexusDefBlock>

Add types to your GraphQL Schema. The available nexus definition block functions include objectType inputObjectType enumType and so on. Refer to the official Nexus API documentation for more information about these functions.

Example

// schema.ts

import { app } from 'graphql-santa'

app.objectType({
  name: 'Foo',
  definition(t) {
    t.id('id')
  },
})

app.server.start

Start the server. If you don't call this graphql-santa will. Usually you should not have to call it. Please share your use-case with us if you do!

CLI

Development

Overview

yarn
yarn test
yarn dev

Testing

Integration tests rely on npm link. This means those integration tests cannot work on a machine that has not done npm link inside the root of the cloned repo.

The reason we do not use yarn link is that yarn does not symlink the bin into local node_modules.

Working With Example Apps via Linking

Refer to https://github.com/prisma-labs/graphql-santa-examples

Working with create command

In any example you can use this workflow:

rm -rf test-create && mcd test-create && ../node_modules/.bin/graphql-santa create