All Projects → hasib32 → Rest Api With Lumen

hasib32 / Rest Api With Lumen

Rest API boilerplate for Lumen micro-framework.

Projects that are alternatives of or similar to Rest Api With Lumen

Restful Api With Laravel Definitive Guide
Repository with the base code for the course "RESTful API with Laravel - Definitive-Guide"
Stars: ✭ 156 (-66.38%)
Mutual labels:  rest-api, laravel, fractal, oauth2
Api Restful Con Laravel Guia Definitiva
Repositorio para el código base del curso "API RESTful con Laravel - Guía Definitiva"
Stars: ✭ 95 (-79.53%)
Mutual labels:  rest-api, laravel, fractal, oauth2
Laravel Fractal
An easy to use Fractal wrapper built for Laravel and Lumen applications
Stars: ✭ 1,748 (+276.72%)
Mutual labels:  laravel, lumen, fractal
Jikan Rest
The REST API for Jikan
Stars: ✭ 200 (-56.9%)
Mutual labels:  rest-api, laravel, lumen
Lumen Generators
A collection of generators for Lumen and Laravel 5.
Stars: ✭ 339 (-26.94%)
Mutual labels:  rest-api, laravel, lumen
Project
⭐️ Antares Project Application Skeleton. This is the very first place you should start. It allows you to create a brand new awesome project in easy few steps.
Stars: ✭ 84 (-81.9%)
Mutual labels:  rest-api, laravel, boilerplate
Laravel Responder
A Laravel Fractal package for building API responses, giving you the power of Fractal with Laravel's elegancy.
Stars: ✭ 673 (+45.04%)
Mutual labels:  laravel, lumen, fractal
Laravel Api Boilerplate
A Boilerplate Project For Laravel API's (NOT MAINTAINED)
Stars: ✭ 113 (-75.65%)
Mutual labels:  rest-api, laravel, boilerplate
Forrest
A Laravel library for Salesforce
Stars: ✭ 171 (-63.15%)
Mutual labels:  rest-api, laravel, lumen
Lumen Microservice
Lumen on Docker - Skeleton project with Nginx, MySQL & PHP 7 | Aws ECS, Google Kubernates, Azure Container Engine
Stars: ✭ 183 (-60.56%)
Mutual labels:  rest-api, laravel, lumen
Laravel Postal Code Validation
Worldwide postal code validation for Laravel and Lumen
Stars: ✭ 278 (-40.09%)
Mutual labels:  laravel, lumen
lumen-oauth2
OAuth2 module for the Lumen PHP framework.
Stars: ✭ 29 (-93.75%)
Mutual labels:  oauth2, lumen
Laravel Job Status
Add ability to track Job progress, status and result dispatched to Queue.
Stars: ✭ 279 (-39.87%)
Mutual labels:  laravel, lumen
Laravel Api Boilerplate
Laravel API Boilerplate | Please consult the Wiki !
Stars: ✭ 300 (-35.34%)
Mutual labels:  laravel, boilerplate
Laravel Swap
💵 Currency exchange rates for Laravel and Lumen
Stars: ✭ 296 (-36.21%)
Mutual labels:  laravel, lumen
Yii2 Angular Boilerplate
Yii2 REST API + Angular10 Boilerplate (Frontend/Backend)
Stars: ✭ 194 (-58.19%)
Mutual labels:  rest-api, boilerplate
Node Rem
Node REM - NodeJS Rest Express MongoDB and more: typescript, passport, JWT, socket.io, HTTPS, HTTP2, async/await, nodemailer, templates, pagination, docker, etc. Live Demo: https://node-rem-ngduc.vercel.app
Stars: ✭ 192 (-58.62%)
Mutual labels:  rest-api, boilerplate
Laravel5 Jsonapi
Laravel 5 JSON API Transformer Package
Stars: ✭ 313 (-32.54%)
Mutual labels:  laravel, lumen
Laravel S
LaravelS is an out-of-the-box adapter between Swoole and Laravel/Lumen.
Stars: ✭ 3,479 (+649.78%)
Mutual labels:  laravel, lumen
Jwt Auth Guard
JWT Auth Guard for Laravel and Lumen Frameworks.
Stars: ✭ 319 (-31.25%)
Mutual labels:  laravel, lumen

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'       => '[email protected]',
    'middleware' => "scope:messages,messages:create"
]);
$route->get('messages',  [
    'uses'       => '[email protected]',
    'middleware' => "scope:messages,messages:list"
]);
$route->get('messages/{id}', [
    'uses'       => '[email protected]',
    'middleware' => "scope:messages,messages:read"
]);
$route->put('messages/{id}', [
    'uses'       => '[email protected]',
    'middleware' => "scope:messages,messages:write"
]);
$route->delete('messages/{id}', [
    'uses'       => '[email protected]',
    '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

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].