JWT-Authenticated API with Slim Framework
Build a REST API for managing tasks with the Slim micro-framework for PHP, where every user registers, logs in to get a JSON Web Token (JWT), and then can only see and change their own tasks. Passwords are hashed securely, tokens expire, and a middleware checks the token on every protected request. Data is stored in a database through PDO with prepared statements. It's a good introduction to building APIs without a big framework: you'll learn routing and middleware in Slim, dependency injection with PHP-DI, secure password storage, stateless authentication with JWTs using firebase/php-jwt, ownership checks, and testing an API with PHPUnit.
- Estimate
- ~9h
- Steps
- 5
- Completed by
- 0
- Proposed by
- codeseed.app
Slim Framework · firebase/php-jwt · PDO
Project roadmap
- 01
Set up Slim and routing
~1.5hCreate a project with Composer and install slim/slim, slim/psr7, php-di/php-di and firebase/php-jwt. Set up the app with the body parsing and error middleware, a container with the settings (database connection and JWT secret from environment variables) and a PDO service, and a GET /health route. Create the users and tasks tables with a SQL script. Done when: the API starts and /health returns JSON.
- 02
Register and log in
~2hAdd POST /register (validate the email and a password of at least 10 characters, store it with password_hash, and return 409 for an email already in use) and POST /login (check with password_verify and return a JWT signed with HS256 containing the user id and an expiry of 1 hour). Give the same error for a wrong email or password. Done when: users can register and log in to receive a token.
- 03
Write the JWT middleware
~1.5hWrite a middleware that reads the Authorization: Bearer header, decodes the token with JWT::decode and a Key object (which checks the signature and expiry), and adds the user id to the request as an attribute. Return 401 with a JSON error for missing, invalid or expired tokens. Apply it to a route group for the task endpoints. Done when: protected routes reject requests without a valid token.
- 04
Build the task endpoints
~2hAdd GET /tasks (the user's tasks, with ?done= filtering and pagination), POST /tasks, GET, PUT and DELETE /tasks/{id}. Every query includes the user id, so users can never reach others' tasks (return 404 for them). Validate the input (title 1 to 200 characters) and return 201, 204, 400 and 404 correctly. Done when: all task endpoints work, and one user can't see another's tasks.
- 05
Test the auth flow
~2hWrite PHPUnit tests that create the app with a test SQLite database and send requests to it (creating PSR-7 requests directly): registering, duplicate emails, logging in with right and wrong passwords, expired and tampered tokens, task CRUD, and ownership rules between two users. Done when: vendor/bin/phpunit passes.
Resources
Ready to build this?
Get a GitHub repo and start building. Your AI reviewer checks each step as you go.
Tech stack