Build the mental model
Turn Project 1's Blog API into something production-ready by adding an authentication layer (token decoding in context) and per-user rate limiting (a window-based counter per mutation) — combining lessons 16/17's context and authorization patterns with lesson 20's abuse-prevention ideas into one project. Build the rate-limit check as a reusable resolver-level wrapper function.
Connect it to a real scenario
Guard the `createPost` mutation with `requireAuth(context)` and use a `checkRateLimit` helper to cap each user at five posts per hour. Return an error with `TOO_MANY_REQUESTS` in its extensions when the limit is exceeded, so the frontend can show a retry-after message.
Try the working example
const resolvers = {
Mutation: {
createPost: async (
_parent: unknown,
args: PostInput,
context: Context,
) => {
requireAuth(context);
await checkRateLimit(`post:create:${context.userId}`, {
max: 5,
windowSeconds: 3600,
});
return context.db.post.create({
data: { ...args, authorId: context.userId },
});
},
},
};You can operate the Blog API as an auth-protected, rate-limited, production-facing service.5-minute try-it
Add auth and a rate limit (for example, 20 comments per user per hour) to the `addComment` mutation as well.
One important caution
Keeping the rate-limit counter in process-local memory (a plain object or Map) breaks down across multiple server instances, since each instance counts independently and lets more requests through than the intended limit — use a shared store like Redis.