Table of Contents
Authentication processing
Prerequisites
Dependencies
Setting up directory structure and development environment
Database settings
Integrate schema and parser with GraphQL server
model
Parser
server
Test API
Mutation for registered users
Login Mutation
Query for individual users
Summarize
Home Web Front-end CSS Tutorial Let's Create Our Own Authentication API with Nodejs and GraphQL

Let's Create Our Own Authentication API with Nodejs and GraphQL

Apr 02, 2025 am 04:22 AM

Let's Create Our Own Authentication API with Nodejs and GraphQL

Authentication is one of the most challenging tasks for developers new to GraphQL. There are many technical considerations involved, including choosing an ORM that is easy to set, how to generate a secure token and hash password, and even which HTTP library to use and how to use it.

This article focuses on local authentication . This is probably the most popular way for modern websites to handle authentication, which is achieved by requesting users’ emails and passwords (as opposed to using Google Authentication).

In addition, this article uses Apollo Server 2, JSON Web Tokens (JWT) and Sequelize ORM to build a Node.js authentication API.

Authentication processing

Log in to the system:

  • Authentication identifies or authenticates users.
  • Authorizes verification of routes (or parts of the application) that an authenticated user can access.

The steps to implement this process are as follows:

  1. Users register with password and email.
  2. The user's credentials are stored in the database.
  3. After registration is completed, the user will be redirected to the login page.
  4. After authentication, the user will be granted access to a specific resource.
  5. The user's status is stored in any browser storage medium (eg, localStorage, cookies, sessions) or in a JWT.

Prerequisites

Before you go deeper, here are some steps you need to follow.

  • Node.js 6 or higher
  • Yarn (recommended) or NPM
  • GraphQL Playground
  • Basics of GraphQL and Node.js
  • …A heart that seeks knowledge!

Dependencies

Here is a long list, let's get started:

  • Apollo Server : An open source GraphQL server compatible with any type of GraphQL client. In this project, we will not use Express as our server. Instead, we will take advantage of the capabilities of Apollo Server to expose our GraphQL API.
  • bcryptjs : We want to hash the user password into our database. That's why we're going to use bcrypt. It relies on the getRandomValues ​​interface of the Web Crypto API to get secure random numbers.
  • dotenv : We will use dotenv to load environment variables from our .env file.
  • jsonwebtoken : After the user logs in, each subsequent request will contain a JWT, allowing the user to access the routes, services, and resources allowed using the token. jsonwebtoken will be used to generate JWTs, which are used to verify the user's identity.
  • nodemon : A tool that helps develop Node-based applications by automatically restarting Node applications when a directory change is detected. We don't want the server to be shut down and started every time the code changes. Nodemon checks for changes in our application every time and automatically restarts the server.
  • mysql2 : Node.js' SQL client. We need it to connect to our SQL server so that we can run the migration.
  • sequelize : Sequelize is a Promise-based Node ORM used in Postgres, MySQL, MariaDB, SQLite and Microsoft SQL Server. We will use Sequelize to automatically generate our migrations and models.
  • sequelize cli : We will use the Sequelize CLI to run the Sequelize command. Install it globally in the terminal using yarn add --global sequelize-cli.

Setting up directory structure and development environment

Let's create a brand new project. Create a new folder and create the following in it:

 <code>yarn init -y</code>
Copy after login

The -y flag means we select yes for all yarn init issues and use the default value.

We should also place a package.json file in the folder, so let's install the project dependencies:

 <code>yarn add apollo-server bcryptjs dotenv jsonwebtoken nodemon sequelize sqlite3</code>
Copy after login

Next, let's add Babel to our development environment:

 <code>yarn add babel-cli babel-preset-env babel-preset-stage-0 --dev</code>
Copy after login

Now, let's configure Babel. Run touch .babelrc in the terminal. This will create and open a Babel configuration file where we will add the following:

 <code>{ "presets": ["env", "stage-0"] }</code>
Copy after login

It would be even better if our servers started and migrated data. We can do this automatically by updating package.json with the following:

 <code>"scripts": { "migrate": " sequelize db:migrate", "dev": "nodemon src/server --exec babel-node -e js", "start": "node src/server", "test": "echo \"Error: no test specified\" && exit 1" },</code>
Copy after login

Here is our current complete package.json file:

 <code>{ "name": "graphql-auth", "version": "1.0.0", "main": "index.js", "scripts": {  "migrate": " sequelize db:migrate",  "dev": "nodemon src/server --exec babel-node -e js",  "start": "node src/server",  "test": "echo \"Error: no test specified\" && exit 1" }, "dependencies": {  "apollo-server": "^2.17.0",  "bcryptjs": "^2.4.3",  "dotenv": "^8.2.0",  "jsonwebtoken": "^8.5.1",  "nodemon": "^2.0.4",  "sequelize": "^6.3.5",  "sqlite3": "^5.0.0" }, "devDependencies": {  "babel-cli": "^6.26.0",  "babel-preset-env": "^1.7.0",  "babel-preset-stage-0": "^6.24.1" } }</code>
Copy after login

Now that our development environment is set up, let's move to the database, we'll store things there.

Database settings

We will use MySQL as our database and use Sequelize ORM for relational mapping. Run sequelize init (assuming you have installed it globally before). This command should create three folders: /config /models and /migrations. At this time, our project directory structure is forming.

Let's configure our database. First, create a .env file in the project root directory and paste the following:

 <code>NODE_ENV=development DB_HOST=localhost DB_USERNAME= DB_PASSWORD= DB_NAME=</code>
Copy after login

Then go to the /config folder we just created and rename the config.json file in it to config.js. Then, put the following code into it:

 <code>require('dotenv').config() const dbDetails = { username: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, host: process.env.DB_HOST, dialect: 'mysql' } module.exports = { development: dbDetails, production: dbDetails }</code>
Copy after login

Here we are reading the database details we set in the .env file. process.env is a global variable injected by Node to represent the current state of the system environment.

Let's update our database details with the appropriate data. Open the SQL database and create a table named graphql_auth. I use Laragon as my local server and use phpmyadmin to manage the database tables.

No matter what you use, we need to update the .env file with the latest information:

 <code>NODE_ENV=development DB_HOST=localhost DB_USERNAME=graphql_auth DB_PASSWORD= DB_NAME=</code>
Copy after login

Let's configure Sequelize. Create a .sequelizerc file in the root directory of the project and paste the following:

 <code>const path = require('path') module.exports = { config: path.resolve('config', 'config.js') }</code>
Copy after login

Now let's integrate our configuration into the model. Go to index.js in /models folder and edit the config variable.

 <code>const config = require(__dirname '/../../config/config.js')[env]</code>
Copy after login

Finally, let's write our model. For this project, we need a User model. Let's use Sequelize to automatically generate the model. Here is what we need to run in the terminal to set it up:

 <code>sequelize model:generate --name User --attributes username:string,email:string,password:string</code>
Copy after login

Let's edit the model it creates for us. Go to user.js in /models folder and paste the following:

 <code>'use strict'; module.exports = (sequelize, DataTypes) => { const User = sequelize.define('User', {  username: {   type: DataTypes.STRING,  },  email: {   type: DataTypes.STRING,   },  password: {   type: DataTypes.STRING,  } }, {}); return User; };</code>
Copy after login

Here we create properties and fields for usernames, emails, and passwords. Let's run the migration to track changes in our schema:

 <code>yarn migrate</code>
Copy after login

Now let's write the pattern and parser.

Integrate schema and parser with GraphQL server

In this section, we will define our patterns, write parser functions, and expose them to our server.

model

In the src folder, create a new folder named /schema and create a file named schema.js in it. Paste the following code:

 <code>const { gql } = require('apollo-server') const typeDefs = gql` type User {  id: Int!  username: String  email: String! } type AuthPayload {  token: String!  user: User! } type Query {  user(id: Int!): User  allUsers: [User!]!  me: User } type Mutation {  registerUser(username: String, email: String!, password: String!): AuthPayload!  login (email: String!, password: String!): AuthPayload! } ` module.exports = typeDefs</code>
Copy after login

Here, we import graphql-tag from apollo-server. Apollo Server needs to wrap our pattern with gql.

Parser

In the src folder, create a new folder named /resolvers and create a file named resolver.js in it. Paste the following code:

 <code>const bcrypt = require('bcryptjs') const jsonwebtoken = require('jsonwebtoken') const models = require('../models') require('dotenv').config() const resolvers = {  Query: {   async me(_, args, { user }) {    if(!user) throw new Error('You are not authenticated')    return await models.User.findByPk(user.id)   },   async user(root, { id }, { user }) {    try {     if(!user) throw new Error('You are not authenticated!')     return models.User.findByPk(id)    } catch (error) {     throw new Error(error.message)    }   },   async allUsers(root, args, { user }) {    try {     if (!user) throw new Error('You are not authenticated!')     return models.User.findAll()    } catch (error) {     throw new Error(error.message)    }   }  },  Mutation: {   async registerUser(root, { username, email, password }) {    try {     const user = await models.User.create({      username,      email,      password: await bcrypt.hash(password, 10)     })     const token = jsonwebtoken.sign(      { id: user.id, email: user.email},      process.env.JWT_SECRET,      { expiresIn: '1y' }     )     return {      token, id: user.id, username: user.username, email: user.email, message: "Authentication succesfull"     }    } catch (error) {     throw new Error(error.message)    }   },   async login(_, { email, password }) {    try {     const user = await models.User.findOne({ where: { email }})     if (!user) {      throw new Error('No user with that email')     }     const isValid = await bcrypt.compare(password, user.password)     if (!isValid) {      throw new Error('Incorrect password')     }     // return jwt     const token = jsonwebtoken.sign(      { id: user.id, email: user.email},      process.env.JWT_SECRET,      { expiresIn: '1d'}     )     return {      token, user     }   } catch (error) {    throw new Error(error.message)   }  } }, } module.exports = resolvers</code>
Copy after login

There is a lot of code, let's see what's going on there.

First, we import our model, bcrypt, and jsonwebtoken, and then initialize our environment variables.

Next is the parser function. In the query parser, we have three functions (me, user, and allUsers):

  • Me query to get detailed information of the currently logged in user. It accepts user objects as context parameters. The context is used to provide access to our database, which is used to load user data through the ID provided in the query.
  • The user query obtains the user's detailed information based on the user's ID. It accepts id as context parameter and a user object.
  • The alluser query returns the details of all users.

If the user status is logged in, user will be an object; if the user is not logged in, user will be null. We will create this user in our mutation.

In the mutation parser, we have two functions (registerUser and loginUser):

  • registerUser accepts the user's username, email, and password and uses these fields to create a new row in our database. It should be noted that we use the bcryptjs package to hash the user's password using bcrypt.hash(password, 10). jsonwebtoken.sign synchronously signs the given payload to a JSON Web Token string (in this case user ID and email). Finally, registerUser will return the JWT string and user profile if successful; if an error occurs, an error message will be returned.
  • login accepts email and password and checks if these details match the provided details. First, we check if the email value already exists somewhere in the user database.
 <code>models.User.findOne({ where: { email }}) if (!user) { throw new Error('No user with that email') }</code>
Copy after login

Then, we use bcrypt's bcrypt.compare method to check if the password matches.

 <code>const isValid = await bcrypt.compare(password, user.password) if (!isValid) { throw new Error('Incorrect password') }</code>
Copy after login

Then, like we did in registerUser before, we use jsonwebtoken.sign to generate the JWT string. login mutation returns token and user objects.

Now let's add JWT_SECRET to our .env file.

 <code>JWT_SECRET=一个非常长的秘密</code>
Copy after login

server

Finally, it's the server! Create a server.js in the root folder of the project and paste the following:

 <code>const { ApolloServer } = require('apollo-server') const jwt =  require('jsonwebtoken') const typeDefs = require('./schema/schema') const resolvers = require('./resolvers/resolvers') require('dotenv').config() const { JWT_SECRET, PORT } = process.env const getUser = token => { try {  if (token) {   return jwt.verify(token, JWT_SECRET)  }  return null } catch (error) {  return null } } const server = new ApolloServer({ typeDefs, resolvers, context: ({ req }) => {  const token = req.get('Authorization') || ''  return { user: getUser(token.replace('Bearer', ''))} }, introspection: true, playground: true }) server.listen({ port: process.env.PORT || 4000 }).then(({ url }) => { console.log(`? Server ready at ${url}`); });</code>
Copy after login

Here we import schema, resolvers, and jwt and initialize our environment variables. First, we use verify to verify the JWT token. jwt.verify accepts token and JWT key as parameters.

Next, we create our server using an ApolloServer instance that accepts typeDefs and resolvers.

We have a server! Let's start it by running yarn dev in the terminal.

Test API

Now let's test the GraphQL API using GraphQL Playground. We should be able to register, log in and view all users – including individual users – through ID.

We will first open the GraphQL Playground app, or simply open localhost://4000 in the browser to access it.

Mutation for registered users

 <code>mutation { registerUser(username: "Wizzy", email: "[email protected]", password: "wizzyekpot" ){  token } }</code>
Copy after login

We should get results like this:

 <code>{ "data": {  "registerUser": {   "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTUsImVtYWlsIjoiZWtwb3RAZ21haWwuY29tIiwiaWF0IjoxNTk5MjQwMzAwLCJleHAiOjE2MzA3OTc5MDB9.gmeynGR9Zwng8cIJR75Qrob9bovnRQT242n6vfBt5PY"  } } }</code>
Copy after login

Login Mutation

Now let's log in with the user details we just created:

 <code>mutation { login(email:"[email protected]" password:"wizzyekpot"){  token } }</code>
Copy after login

We should get results like this:

 <code>{ "data": {  "login": {   "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTUsImVtYWlsIjoiZWtwb3RAZ21haWwuY29tIiwiaWF0IjoxNTk5MjQwMzcwLCJleHAiOjE1OTkzMjY3NzB9.PDiBKyq58nWxlgTOQYzbtKJ-HkzxemVppLA5nBdm4nc"  } } }</code>
Copy after login

marvelous!

Query for individual users

In order to query a single user, we need to pass the user token as an authorization header. Go to the HTTP Header tab.

...and paste this content in:

 <code>{ "Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTUsImVtYWlsIjoiZWtwb3RAZ21haWwuY29tIiwiaWF0IjoxNTk5MjQwMzcwLCJleHAiOjE1OTkzMjY3NzB9.PDiBKyq58nWxlgTOQYzbtKJ-HkzxemVppLA5nBdm4nc" }</code>
Copy after login

Here is the query:

 <code>query myself{ me {  id  email  username } }</code>
Copy after login

We should get results like this:

 <code>{ "data": {  "me": {   "id": 15,   "email": "[email protected]",   "username": "Wizzy"  } } }</code>
Copy after login

Great! Now let's get the user by ID:

 <code>query singleUser{ user(id:15){  id  email  username } }</code>
Copy after login

Here is a query to get all users:

 <code>{ allUsers{  id  username  email } }</code>
Copy after login

Summarize

Authentication is one of the most difficult tasks when building a website that requires authentication. GraphQL enables us to build a complete authentication API using only one endpoint. Sequelize ORM makes it so easy to create relationships with SQL databases that we hardly have to worry about our model. It is also worth noting that we do not need HTTP server libraries (such as Express) but use Apollo GraphQL as middleware. Apollo Server 2 now enables us to create our own library-independent GraphQL servers!

Please check the source code for this tutorial on GitHub.

The above is the detailed content of Let's Create Our Own Authentication API with Nodejs and GraphQL. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Article

R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
1 months ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Best Graphic Settings
4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. How to Fix Audio if You Can't Hear Anyone
1 months ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Chat Commands and How to Use Them
1 months ago By 尊渡假赌尊渡假赌尊渡假赌

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Working With GraphQL Caching Working With GraphQL Caching Mar 19, 2025 am 09:36 AM

If you’ve recently started working with GraphQL, or reviewed its pros and cons, you’ve no doubt heard things like “GraphQL doesn’t support caching” or

Building an Ethereum app using Redwood.js and Fauna Building an Ethereum app using Redwood.js and Fauna Mar 28, 2025 am 09:18 AM

With the recent climb of Bitcoin’s price over 20k $USD, and to it recently breaking 30k, I thought it’s worth taking a deep dive back into creating Ethereum

Creating Your Own Bragdoc With Eleventy Creating Your Own Bragdoc With Eleventy Mar 18, 2025 am 11:23 AM

No matter what stage you’re at as a developer, the tasks we complete—whether big or small—make a huge impact in our personal and professional growth.

Vue 3 Vue 3 Apr 02, 2025 pm 06:32 PM

It&#039;s out! Congrats to the Vue team for getting it done, I know it was a massive effort and a long time coming. All new docs, as well.

A bit on ci/cd A bit on ci/cd Apr 02, 2025 pm 06:21 PM

I&#039;d say "website" fits better than "mobile app" but I like this framing from Max Lynch:

Can you get valid CSS property values from the browser? Can you get valid CSS property values from the browser? Apr 02, 2025 pm 06:17 PM

I had someone write in with this very legit question. Lea just blogged about how you can get valid CSS properties themselves from the browser. That&#039;s like this.

Stacked Cards with Sticky Positioning and a Dash of Sass Stacked Cards with Sticky Positioning and a Dash of Sass Apr 03, 2025 am 10:30 AM

The other day, I spotted this particularly lovely bit from Corey Ginnivan’s website where a collection of cards stack on top of one another as you scroll.

Let's use (X, X, X, X) for talking about specificity Let's use (X, X, X, X) for talking about specificity Mar 24, 2025 am 10:37 AM

I was just chatting with Eric Meyer the other day and I remembered an Eric Meyer story from my formative years. I wrote a blog post about CSS specificity, and

See all articles