Creating a GraphQL API with Node.js easily
A Node.js framework for creating GraphQL API servers easily and without a lot of boilerplate.
The core server code of any GraphQL API it’s always basically the same, create the HTTP server, get your resolvers and schema, set the /graphql
endpoint, in development set the endpoint for GraphiQL and if you are going to use subscriptions also create a subscription server and attach it to your HTTP server.
That’s a lot of boilerplate code! And Grial will handle all of that for you and let you only think about your application business logic and API layer. How? Grial it’s a serie of many little libraries, the core are @grial/cli and @grial/server which would create your API server and run it with a single command.
Grial also have many connectors little libraries that allow you to easily connect your API to different data sources and enable the creation of a big API that consumes data from Redis, MongoDB, Rest APIs, file disk, etc.
Let’s code an API!
Time to code, we’ll create a directory for the API and install this modules.
yarn add @grial/cli @grial/server @grial/connector-faker
The last one it’s a connector for the faker module and we are going to use it to fake our API data. Then we are going to add the following script to our package.json
file.
{
"scripts": {
"start": "grial start"
}
}
That script will use @grial/cli
to run our @grial/server
, it’ll also try to read our environment variables from a .env
file, we can then create one with this basic variables.
PORT=8000
By default it will set PORT=3000
, every Grial module have the required and possible environment variables in the readme.
Define the schema
After that we are ready to write our API code, let’s define our schema, Grial let us write it inside a schema.gql
or a schema.graphql
file, so we’re going to define something like this in one of them.
type User {
id: Int!
username: String!
firstName: String!
lastName: String!
fullName: String!
bio: String!
}
type Query {
me: User!
}
schema {
query: Query
}
Write the resolvers
Now we need to create a resolvers.js
files. Along with the schema these are the only required files.
// main resolvers
exports.Query = {
me(rootQuery, args, context) {
return context.models.User.me();
}
};
// type resolvers
exports.User = {
fullName(user) {
return `${user.firstName} ${user.lastName}`;
}
};
We exported many keys but we could also use module.exports
and just export a single object with the keys Query and User.
My own personal recommendation is to use many exports, also as your resolvers grow create a
resolvers
directory with a fileQuery.js
,Mutation.js
,Subscription.js
and one file for each type we need a custom resolver.
Create the models
As we saw we received the model User
from our context
object, Grial automatically will read our models.js
file and instantiate each one. We will create this file now.
exports.User = async function User({ faker }) {
return {
me() {
return {
id: faker.random.number(),
username: faker.internet.userName(),
firstName: faker.name.firstName(),
lastName: faker.name.lastName(),
bio: faker.name.title()
};
}
};
};
That’s our User
model, again we used a name export but we could have done a single export with an object. If you check we are creating an async function for our model, that’s because we could require to run asynchronous code before creating our resolvers (maybe to synchronize the model with a database).
Import the connectors
We also received faker
in our model, that’s our connector, each model receive a single parameter with each environment variable and connector that of course allow a single model to get data using multiple connectors.
So, we receive the connector, but how Grial knows that? Simple, just create a connectors.js
object and export each connector you want to use.
exports.faker = require('@grial/connector-faker');
Those are our API connectors, we can use this file to also define custom connectors, maybe using third-party APIs clients. Every connector will receive every environment variable and it’s going to use some of them, the Faker connector use FAKER_LOCALE
and FAKER_SEED
and have default values.
If you want to use the same connector multiple times you can wrap them with a high order function which receive the environment variables and pass new ones.
exports.faker = env => require('@grial/connector-faker')({
FAKER_LOCALE: env.DATA_LOCALE,
FAKER_SEED: env.DATA_SEED
})
Running the app
With that done we have our API code ready, just run yarn start
or npm start
and you will see something like this in your terminal.
$ grial start
Grial server running
> GraphiQL Endpoint = http://localhost:8000/ide
> API Endpoint = http://localhost:8000/graphql
> Subscriptions Endpoint = http://localhost:8000/subscriptions
We can then access to http://localhost:3000/ide
and try the API. As you can see Grial also set you a subscriptions endpoint by default, if you create a PubSub instance and add subscriptions to your schema and resolvers you can start using them without worries.
You can also try this application running in production accessing to https://grial-example-basic.now.sh/ide and another API built with Grial fetching data from a Rest API https://grial-example-rest-wrapper.now.sh/ide.
The second example wrap the JSONPlaceholder API into a GraphQL one and add tests for the models and resolvers.
Final words
Grial allow to customize its behavior using a grial.config.js
file with the keys graphqlConfig
, graphiqlConfig
and subscriptionConfig
, they let you customize GraphQL, GraphiQL and the subscription server configurations, useful to include some token based authentication and other features.
It’s also pluggable, you can use the programmatically API to integrate it inside any HTTP server or application, it can be Next.js, Express.js, etc.
You can contribute to Grial and help create an awesome GraphQL framework with more connectors, features, tools, examples and a completed documentation.
Top comments (3)
It's taking all my strength not to leave work right now to go home and learn this. I didn't think GraphQL could get much easier. I was wrong.
If someone have any question or feedback about Grial I'll love to hear.
Nice article!
quick note: PORT number should be replaced :)