Rest API boilerplate for Lumen micro-framework.

Overview

REST API with Lumen 5.5 Build Status

A RESTful API boilerplate for Lumen micro-framework. Features included:

  • Users Resource
  • OAuth2 Authentication using Laravel Passport
  • Scope based Authorization
  • Validation
  • Repository Pattern
  • API Response with Fractal
  • Pagination
  • Seeding Database With Model Factory
  • Event Handling
  • Sending Mail using Mailable class
  • CORS Support
  • Rate Limit API Requests
  • Endpoint Tests and Unit Tests
  • Build Process with Travis CI

Getting Started

First, clone the repo:

$ git clone [email protected]:hasib32/rest-api-with-lumen.git

Laravel Homestead

You can use Laravel Homestead globally or per project for local development. Follow the Installation Guide.

Install dependencies

$ cd rest-api-with-lumen
$ composer install

Configure the Environment

Create .env file:

$ cat .env.example > .env

If you want you can edit database name, database username and database password.

Migrations and Seed the database with fake data

First, we need connect to the database. For homestead user, login using default homestead username and password:

$ mysql -uhomestead -psecret

Then create a database:

mysql> CREATE DATABASE restapi;

And also create test database:

mysql> CREATE DATABASE restapi_test;

Run the Artisan migrate command with seed:

$ php artisan migrate --seed

Create "personal access" and "password grant" clients which will be used to generate access tokens:

$ php artisan passport:install

You can find those clients in oauth_clients table.

API Routes

HTTP Method Path Action Scope Desciption
GET /users index users:list Get all users
POST /users store users:create Create an user
GET /users/{user_id} show users:read Fetch an user by id
PUT /users/{user_id} update users:write Update an user by id
DELETE /users/{user_id} destroy users:delete Delete an user by id

Note: users/me is a special route for getting current authenticated user. And for all User routes 'users' scope is available if you want to perform all actions.

OAuth2 Routes

Visit dusterio/lumen-passport to see all the available OAuth2 routes.

Creating access_token

Since Laravel Passport doesn't restrict any user creating any valid scope. I had to create a route and controller to restrict user creating access token only with permitted scopes. For creating access_token we have to use the accessToken route. Here is an example of creating access_token for grant_type password with Postman.

http://stackoverflow.com/questions/39436509/laravel-passport-scopes

access_token creation

Creating a New Resource

Creating a new resource is very easy and straight-forward. Follow these simple steps to create a new resource.

Step 1: Create Route

Create a new route name messages. Open the routes/web.php file and add the following code:

$route->post('messages', [
    'uses'       => 'MessageController@store',
    'middleware' => "scope:messages,messages:create"
]);
$route->get('messages',  [
    'uses'       => 'MessageController@index',
    'middleware' => "scope:messages,messages:list"
]);
$route->get('messages/{id}', [
    'uses'       => 'MessageController@show',
    'middleware' => "scope:messages,messages:read"
]);
$route->put('messages/{id}', [
    'uses'       => 'MessageController@update',
    'middleware' => "scope:messages,messages:write"
]);
$route->delete('messages/{id}', [
    'uses'       => 'MessageController@destroy',
    'middleware' => "scope:messages,messages:delete"
]);

For more info please visit Lumen Routing page.

Step 2: Create Model and Migration for the Table

Create Message Model inside App/Models directory and create migration using Lumen Artisan command.

Message Model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Message extends Model
{
    /**
     * The database table used by the model.
     *
     * @var string
     */
    protected $table = 'messages';

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'uid',
        'userId',
        'subject',
        'message',
    ];
}

Visit Laravel Eloquent Page for more info about Model.

Create migration for messages table

php artisan make:migration create_messages_table --create=messages

Migration file

class CreateMessagesTable extends Migration
{
    public function up()
    {
        Schema::create('messages', function (Blueprint $table) {
            $table->increments('id');
            $table->string('uid', 36)->unique();
            $table->integer('userId')->unsigned();
            $table->string('subject')->nullable();
            $table->longText('message');
            $table->timestamps();

            $table->foreign('userId')
                ->references('id')->on('users')
                ->onDelete('cascade')
                ->onUpdate('cascade');
        });
    }
}

For more info visit Laravel Migration page.

Step 3: Create Repository

Create MessageRepository and implementation of the repository name EloquentMessageRepository.

MessageRepository

<?php

namespace App\Repositories\Contracts;

interface MessageRepository extends BaseRepository
{
}

EloquentMessageRepository

<?php

namespace App\Repositories;

use App\Models\Message;
use App\Repositories\Contracts\MessageRepository;

class EloquentMessageRepository extends AbstractEloquentRepository implements MessageRepository
{
    /**
     * Model name.
     *
     * @var string
     */
    protected $modelName = Message::class;
}

Next, update RepositoriesServiceProvider to bind the implementation:

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Repositories\Contracts\UserRepository;
use App\Repositories\EloquentUserRepository;
use App\Repositories\Contracts\MessageRepository;
use App\Repositories\EloquentMessageRepository;

class RepositoriesServiceProvider extends ServiceProvider
{
    /**
     * Indicates if loading of the provider is deferred.
     *
     * @var bool
     */
    protected $defer = true;

    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind(UserRepository::class, function () {
            return new EloquentUserRepository(new User());
        });
        $this->app->bind(MessageRepository::class, function () {
            return new EloquentMessageRepository(new Message());
        });
    }

    /**
     * Get the services provided by the provider.
     *
     * @return array
     */
    public function provides()
    {
        return [
            UserRepository::class,
            MessageRepository::class,
        ];
    }
}

Visit Lumen documentation for more info about Service Provider.

Step 4: Create Fractal Transformer

Fractal provides a presentation and transformation layer for complex data output, the like found in RESTful APIs, and works really well with JSON. Think of this as a view layer for your JSON/YAML/etc.

Create a new Transformer name MessageTransformer inside app/Transformers directory:

<?php

namespace App\Transformers;

use App\Models\Message;
use League\Fractal\TransformerAbstract;

class MessageTransformer extends TransformerAbstract
{
    public function transform(Message $message)
    {
        return [
            'id'        => $message->uid,
            'userId'    => $message->userId,
            'subject'   => $message->subject,
            'message'   => $message->message,
            'createdAt' => (string) $message->created_at,
            'updatedAt' => (string) $message->updated_at,
        ];
    }
}

Visit Fractal official page for more information.

Step 5: Create Policy

For authorization we need to create policy that way basic user can't show or edit other user messages.

MessagePolicy

<?php

namespace App\Policies;

use App\Models\User;
use App\Models\Message;

class MessagePolicy
{
    /**
     * Intercept checks.
     *
     * @param User $currentUser
     * @return bool
     */
    public function before(User $currentUser)
    {
        if ($currentUser->isAdmin() && (!$currentUser->tokenCan('basic') || $currentUser->tokenCan('undefined'))) {
            return true;
        }
    }

    /**
     * Determine if a given user has permission to show.
     *
     * @param User $currentUser
     * @param Message $message
     * @return bool
     */
    public function show(User $currentUser, Message $message)
    {
        return $currentUser->id === $message->userId;
    }

    /**
     * Determine if a given user can update.
     *
     * @param User $currentUser
     * @param Message $message
     * @return bool
     */
    public function update(User $currentUser, Message $message)
    {
        return $currentUser->id === $message->userId;
    }

    /**
     * Determine if a given user can delete.
     *
     * @param User $currentUser
     * @param Message $message
     * @return bool
     */
    public function destroy(User $currentUser, Message $message)
    {
        return $currentUser->id === $message->userId;
    }
}

Next, update AuthServiceProvider to use the policy:

Gate::policy(Message::class, MessagePolicy::class);

And add scopes to Passport::tokensCan:

[
    'messages' => 'Messages scope',
    'messages:list' => 'Messages scope',
    'messages:read' => 'Messages scope for reading records',
    'messages:write' => 'Messages scope for writing records',
    'messages:create' => 'Messages scope for creating records',
    'messages:delete' => 'Messages scope for deleting records'
]

Visit Lumen Authorization Page for more info about Policy.

Last Step: Create Controller

Finally, let's create the MessageController. Here we're using MessageRepository, MessageTransformer and MessagePolicy.

<?php

namespace App\Http\Controllers;

use App\Models\Message;
use App\Repositories\Contracts\MessageRepository;
use Illuminate\Http\Request;
use App\Transformers\MessageTransformer;

class MessageController extends Controller
{
    /**
     * Instance of MessageRepository.
     *
     * @var MessageRepository
     */
    private $messageRepository;

    /**
     * Instanceof MessageTransformer.
     *
     * @var MessageTransformer
     */
    private $messageTransformer;

    /**
     * Constructor.
     *
     * @param MessageRepository $messageRepository
     * @param MessageTransformer $messageTransformer
     */
    public function __construct(MessageRepository $messageRepository, MessageTransformer $messageTransformer)
    {
        $this->messageRepository = $messageRepository;
        $this->messageTransformer = $messageTransformer;

        parent::__construct();
    }

    /**
     * Display a listing of the resource.
     *
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function index(Request $request)
    {
        $messages = $this->messageRepository->findBy($request->all());

        return $this->respondWithCollection($messages, $this->messageTransformer);
    }

    /**
     * Display the specified resource.
     *
     * @param $id
     * @return \Illuminate\Http\JsonResponse|string
     */
    public function show($id)
    {
        $message = $this->messageRepository->findOne($id);

        if (!$message instanceof Message) {
            return $this->sendNotFoundResponse("The message with id {$id} doesn't exist");
        }

        // Authorization
        $this->authorize('show', $message);

        return $this->respondWithItem($message, $this->messageTransformer);
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse|string
     */
    public function store(Request $request)
    {
        // Validation
        $validatorResponse = $this->validateRequest($request, $this->storeRequestValidationRules($request));

        // Send failed response if validation fails
        if ($validatorResponse !== true) {
            return $this->sendInvalidFieldResponse($validatorResponse);
        }

        $message = $this->messageRepository->save($request->all());

        if (!$message instanceof Message) {
            return $this->sendCustomResponse(500, 'Error occurred on creating Message');
        }

        return $this->setStatusCode(201)->respondWithItem($message, $this->messageTransformer);
    }

    /**
     * Update the specified resource in storage.
     *
     * @param Request $request
     * @param $id
     * @return \Illuminate\Http\JsonResponse
     */
    public function update(Request $request, $id)
    {
        // Validation
        $validatorResponse = $this->validateRequest($request, $this->updateRequestValidationRules($request));

        // Send failed response if validation fails
        if ($validatorResponse !== true) {
            return $this->sendInvalidFieldResponse($validatorResponse);
        }

        $message = $this->messageRepository->findOne($id);

        if (!$message instanceof Message) {
            return $this->sendNotFoundResponse("The message with id {$id} doesn't exist");
        }

        // Authorization
        $this->authorize('update', $message);


        $message = $this->messageRepository->update($message, $request->all());

        return $this->respondWithItem($message, $this->messageTransformer);
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param $id
     * @return \Illuminate\Http\JsonResponse|string
     */
    public function destroy($id)
    {
        $message = $this->messageRepository->findOne($id);

        if (!$message instanceof Message) {
            return $this->sendNotFoundResponse("The message with id {$id} doesn't exist");
        }

        // Authorization
        $this->authorize('destroy', $message);

        $this->messageRepository->delete($message);

        return response()->json(null, 204);
    }

    /**
     * Store Request Validation Rules
     *
     * @param Request $request
     * @return array
     */
    private function storeRequestValidationRules(Request $request)
    {
       return [
           'userId'     => 'required|exists:users,id',
           'subject'    => 'required',
           'message'    => 'required',
        ];
    }

    /**
     * Update Request validation Rules
     *
     * @param Request $request
     * @return array
     */
    private function updateRequestValidationRules(Request $request)
    {
        return [
            'subject'    => '',
            'message'    => '',
        ];
    }
}

Visit Lumen Controller page for more info about Controller.

Tutorial

To see the step-by-step tutorial how I created this boilerplate please visit our blog devnootes.net.

Contributing

Contributions, questions and comments are all welcome and encouraged. For code contributions submit a pull request.

Credits

Taylor Otwell, Shahriar Mahmood, Fractal, Phil Sturgeon

License

MIT license

Comments
  • Invalid Credentials

    Invalid Credentials

    I am running your tutorial, but I could not get the accessToken. This is the response that I received. I am trying with severals of emails registered in the database (fake data) with the password: test-password but i am getting this response

    { "error": "invalid_credentials", "message": "The user credentials were incorrect." }

    opened by jossydeleon 15
  • Upgrade to lumen 5.5

    Upgrade to lumen 5.5

    Hi,

    The project looks interesting. I'm going to use it for one project, but I'm not sure if I need to use the previous version. Do you have any plans to upgrade it to 5.5? Should I wait or can start using current version.

    Thanks in advance.

    opened by vbezruchkin 11
  • Issue to create access Token

    Issue to create access Token

    Hello,

    first of all a lot of thanks for the tuto on devops ans this repo git; I'm trying to get an access Token with postman:

    img1

    If i'm right, something missing in the "headers" section. And i choose client_id "2", in reference at the table "oauth_clients" for "Password Grant Client".

    What am i do wrong? Could you please help me?

    Regards

    opened by LordLightAngels 9
  • [NEED HELP]

    [NEED HELP] "Insufficient privileges to perform this action", other than users:list

    I've got the response ("Insufficient privileges to perform this action") towards the api_token when try to register or login. i don't know what went wrong register method: https://pastebin.com/Wn2YVTKs login method: https://pastebin.com/bCDScg7B I've had:

    1. migrate and seed
    2. passport:install
    3. register or login and accessing UserController@show,update,destroy by passing the retrieved token
    4. An example accessing UserController@show, got error: apierror

    Note: UserController@store hasn't tested yet

    Update: when I comment the this->authorize() for each given methods, it works. means there's something wrong in the policy. what should I do next?

    opened by handhikadj 8
  • Getting 404 on every request

    Getting 404 on every request

    First of all thanks for putting this boilerplate together.

    I've gone through the setup guide but I keep getting 404's on every request. I'm not running the project using homestead, instead I want to be able to just go to http://localhost/project-name/public but this returns a 404.

    Am I missing a step? Any help is greatly appreciated.

    opened by pattle 7
  • No getting information

    No getting information

    After getting the accessToken without any problem, how can I get another information from API. For example I trying to get the information of current user doing this:

    screenshot from 2017-07-29 11-41-49 screenshot from 2017-07-29 11-43-02

    But I am getting this when I use http://localhost/users/me:

    { "status": 405, "message": "Method Not Allowed" }

    http://localhost/users

    { "status": 401, "message": "Unauthorized" } Also If I want to save new user or update. If someone has done this, can you share it. Thank you

    opened by limaho 5
  • Show how scopes are difened

    Show how scopes are difened

    Based on my issue for scopes #31 this is my pull request. You have basic 'users' scope for all user related stuff. And users:create, users:read..etc. if you want specific scope for a token. For Administrator there is no logic to have 'admin' scope, so if you pass and 'basic' scope to an administrator he will have the same permissions as a nomer users and he will be able to control only his objects. I also updated the Readme file for message entity. If you find any problem please let me know or if you don't agree with my proposal.

    opened by ibpavlov 3
  • Set model for Repositories from the constructor.

    Set model for Repositories from the constructor.

    This pull request is based on the issue here

    I've added a parameter in the repository constructor to be able to pass a child of the base model. I've run all tests before this commit and they all passed. Also, I've updated Readme guides.

    opened by ibpavlov 3
  • update readme

    update readme

    update app\Providers\RepositoriesServiceProvider.php :

    use App\Repositories\Contracts\MessageRepository; use App\Repositories\EloquentMessageRepository;

    opened by dev-chriss 3
  • Target [Illuminate\Contracts\Http\Kernel] is not instantiable.

    Target [Illuminate\Contracts\Http\Kernel] is not instantiable.

    After cloning repo. when i try to run

    php artisan migrate

    i am getting following error

    [Illuminate\Contracts\Container\BindingResolutionException] Target [Illuminate\Contracts\Http\Kernel] is not instantiable.

    php : 7.0.23

    opened by ShayanSolution 2
  • understanding the use of the accesstokencontroller

    understanding the use of the accesstokencontroller

    hello i have a issue with the accesstoken controller

    as far i can understand we are using the token for accesstokencontroller as the token for the entire application right

    that is my login or im missing the point ?

    opened by zeruel01 2
  • How to call event in new Lumen

    How to call event in new Lumen

    In /app/Repositories/EloquentUserRepository.php

    The correct use of Event in Laravel/Lumen is:

    \Event::fire(new UserCreatedEvent($user));

    or

    event(new UserCreatedEvent($user));

    And my IDE dont know who is \Event in the first case (PhpStorm)

    opened by gfernandez-me 0
  • Private/Public Key exchange

    Private/Public Key exchange

    Hi @hasib32

    First of all I would thank you for this boilerplate really awesome.

    Quick Question can you please pont me to the right directions how I can implement the private/public key exchange on your boilerplate? I have very limited info regarding this.

    Thanks.

    opened by kabelo38 1
Owner
Hasan Hasibul
Senior Software Engineer and dad.
Hasan Hasibul
WP React Plugin Boilerplate - WordPress Setting via React and Rest API

WP React Plugin Boilerplate is a starter WordPress plugin to develop WordPress Plugin via React and Rest API. WP React Plugin Boilerplate WP React Plu

Santosh Kunwar 36 Dec 6, 2022
The Laravel Boilerplate Project - https://laravel-boilerplate.com

Laravel Boilerplate (Current: Laravel 8.*) (Demo) Demo Credentials Admin: [email protected] Password: secret User: [email protected] Password: secret Offici

Anthony Rappa 5.4k Jan 4, 2023
Hydra is a zero-config API boilerplate with Laravel Sanctum that comes with excellent user and role management API out of the box

Hydra - Zero Config API Boilerplate with Laravel Sanctum Hydra is a zero-config API boilerplate with Laravel Sanctum and comes with excellent user and

Hasin Hayder 858 Dec 24, 2022
An implementation of Solder in Laravel's Lumen Framework.

LumenSolder What is LumenSolder? An implementation of Solder in Laravel's Lumen Framework. LumenSolder is an application to help compliment and scale

Technic Pack 3 Sep 16, 2020
Lumen PHP Framework (Starter Template)

This repository contains JWT Auth, Form Request, Route List, Redis, RabbitMQ and Mail packages. A ready auth system comes with the project

Yasin Kรถse 1 Feb 11, 2022
Boilerplate for Slim Framework 3

Boilerplate for Slim Framework 3 Boilerplate for getting started with Slim Framework Use this skeleton application to quickly setup and start working

Yudi Purwanto 23 Dec 27, 2022
Boilerplate between the Magento API and ImportExport, so that you can do fast Array/XMLRPC/SOAP based product imports.

Boilerplate between the Magento API and ImportExport, so that you can do fast Array/XMLRPC/SOAP based product imports.

Daniel Sloof 249 May 30, 2022
Kick-start you next Laravel based API with this awesome boilerplate ๐Ÿš€

Laravel API boilerplate ?? An awesome boilerplate for your next Laravel 9 based API. It's only goal is to simply kick-start your API development and p

treblle 130 Dec 23, 2022
Api first backend boilerplate build with laravel ๐ŸŽฏ you can use as a template ๐Ÿ˜‰

Laravel Backend Template i use this as a starting point for my backend projects , it saves time with basic auth functionalities and has code examples

Hijen EL Khalifi 4 Nov 14, 2022
Creating a simple weather web application with Laravel rest API.

Weather Channel Creating a website for weather status, with Laravel restAPI. See the Website Weather.Channel-1.mov Features REST API Invoake Controlle

AmirH.Najafizadeh 3 Jul 31, 2022
Technical test from a company - Laravel 8 | REST Api | Livewire | Boostrap

Contacts CRUD (Laravel 8) (Demo) Introduction A simple CRUD for manage contacts using Laravel 8. All transaccions work through an REST API. Tech stack

Sebastian Sperandio 3 Nov 17, 2022
Automatically Create professional ready to use Laravel REST API for MySQL Database With Postman Docs and JWT Authentication

Laravel Simple Rest API Generator An API Boilerplate to create a ready-to-use REST API in seconds with Laravel 8.x Install with Composer $ curl -s

null 5 Mar 12, 2022
A Laravel REST API backend with React/Redux, hot module reloading in development and route-level code splitting

React Laravel Boilerplate This is the boilerplate that I personally use for getting projects off the ground quickly using my favourite stack of techno

Carwyn Stephen 174 Jan 6, 2023
laravel adminlte with crud upload photo, ckeditor, validation & rest api

laravel crud with adminlte + restapi feature : Auth crud product (datatable, upload product using ajax, description with ckeditor) crud category displ

Dani 0 Jan 6, 2022
Project skeleton generator for Laravel & Lumen projects

Skeletor Skeletor is a PHP based CLI tool that has been built to take away the pain of setting up a base project skeleton for Laravel & Lumen projects

Wouter 39 Oct 4, 2022
Microservice for sending emails with Lumen

Microservice for send email Microservice developed in Lumen for sending emails in real time or by queue. Current Features Send email in real time Send

Rodrigo Lacerda 75 Jan 9, 2023
LaraAdmin is a Open source Laravel Admin Panel / CMS which can be used as Admin Backend, Data Management Tool or CRM boilerplate for Laravel with features like Advanced CRUD Generation, Module Manager, Backups and many more.

LaraAdmin 1.0 LaraAdmin is a Open source CRM for quick-start Admin based applications with features like Advanced CRUD Generation, Schema Manager and

Dwij IT Solutions 1.5k Dec 29, 2022
A Laravel 5 package that switchs default Laravel scaffolding/boilerplate to AdminLTE template and Pratt Landing Page with Bootstrap 3.0

AdminLTE template Laravel package A Laravel package that switch default Laravel scaffolding / boilerplate to AdminLTE template with Bootstrap 3.0 and

Sergi Tur Badenas 1.8k Jan 3, 2023
:computer: :octocat: A hackathon/MVP boilerplate for laravel web applications. Start your hackathons without hassle.

Laravel Hackathon Starter - SUSUMU ้€ฒ If you have attended any hackathons in the past, then you know how much time it takes to get a project started: d

Prosper Otemuyiwa 1.6k Dec 17, 2022