This project is a boilerplate to build a GraphQL API using NestJS and Prisma, connected to a MySQL database. It includes a Docker setup for the database and an example seed script for initial data population.
- ✅ JWT Authentication (Access + Refresh Tokens)
- ✅ Role-Based Access Control using
@Rolesdecorator andRolesGuard - ✅ Create Account and Login mutations with secure password hashing (bcrypt)
- ✅ Refresh Token Mutation
- ✅ Public GraphQL Queries:
users,user(id) - ✅ Protected Queries:
products: RequiresCHEFroleproductsByUser: RequiresUSERorCHEFrole
- ✅ Global Rate Limiting via Throttler (custom
GqlThrottlerGuard)
- Node.js (recommended LTS version, 16+)
- Nest CLI (optional, for quick scaffolding)
- Docker and Docker Compose (for the MySQL database)
- MySQL Workbench or any other DB GUI tool (optional)
graphql-api-template
├── docker-compose.yml
├── .env
├── package.json
├── prisma
│ ├── schema.prisma
│ └── seed.ts
├── src
│ ├── app.module.ts
│ ├── main.ts
│ └── user
│ ├── user.module.ts
│ ├── user.resolver.ts
│ ├── user.service.ts
│ └── user.type.ts
├── tsconfig.json
└── ...
In the root directory, create a .env file with your database connection URL, for example:
DATABASE_URL="mysql://root:@localhost:3306/graphql-db"datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id Int @id @default(autoincrement())
name String
email String @unique
password String
rol String @default("USER")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
products Product[]
}
model Product {
id Int @id @default(autoincrement())
name String
userId Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
}
npx prisma migrate dev --name initnpx prisma generateprisma/seed.ts
import { PrismaClient } from '@prisma/client'
import * as bcrypt from 'bcryptjs'
const prisma = new PrismaClient()
async function main() {
const salt = await bcrypt.genSalt(10)
const user = await prisma.user.create({
data: {
name: 'Juan Pérez',
email: 's@user.com',
password: await bcrypt.hash('12', salt),
rol: 'USER',
},
})
const chef = await prisma.user.create({
data: {
name: 'María Cocina',
email: 's@chef.com',
password: await bcrypt.hash('12', salt),
rol: 'CHEF',
},
})
await prisma.product.createMany({
data: [
{ name: 'Taco Supremo', userId: user.id },
{ name: 'Burrito Deluxe', userId: user.id },
{ name: 'Paella Gourmet', userId: chef.id },
{ name: 'Sopa Azteca', userId: chef.id },
],
})
}
main()
.catch((e) => {
console.error(e)
process.exit(1)
})
.finally(async () => {
await prisma.$disconnect()
})Run the seed:
npx ts-node prisma/seed.tsnpm install# development
npm run start
# watch mode
npm run start:dev
# production
npm run start:prodThe app will be running at http://localhost:3000.
Go to http://localhost:3000/graphql.
mutation {
createAccount(data: { name: "Juan", email: "juan@example.com", password: "1234" }) {
accessToken
userId
rol
refreshToken
}
}mutation {
login(data: { email: "juan@example.com", password: "1234" }) {
accessToken
userId
rol
refreshToken
}
}mutation {
refreshToken(refreshToken: "yourRefreshTokenHere") {
accessToken
userId
rol
refreshToken
}
}| Query | Roles Required | Description |
|---|---|---|
users |
❌ Public | Get all users |
user(id) |
❌ Public | Get user by ID |
products |
✅ CHEF only | Get all products |
productsByUser |
✅ USER, CHEF | Get products by user ID |
⚠️ productsandproductsByUserrequire anAuthorizationheader with a valid token. Don’t even try without it:
{
"Authorization": "Bearer YOUR_ACCESS_TOKEN"
}# unit tests
npm run test
# e2e tests
npm run test:e2e
# test coverage
npm run test:covnpm install -g @nestjs/mau
mau deployMake sure to customize the .env configuration and never commit sensitive credentials. For production, use secret management tools and containerize the API if needed alongside the DB.