Laravel Lumen GraphQL Server for React Relay Modern

Having Laravel´s Lumen to work as a GraphQL Server able to provide feeds to client applications for Relay Modern its been a beautiful jurney, and i´ll like to share with the world on this publication.

Laravel its Magic, and this is a fact, thanks to its nature we able to do things like this, in this example all work its done thanks to a recently released v2 of an excellent library known as LightHouse, you can find documentation on nuwave/lighthouse, this library it´s in charge some Magic.

On its core uses another library, lets say Lighthouse its a Laravel Wrapper with supercharged powers, on top of webonyx/graphql-php, the real GraphQL implementation under PHP.

As a result of this implementation you able to access and download resuling proyect implementation on this GitHub Repository:

kikoseijo/lumen-lighthouse-graphql

The Jurney starts

Maybe i have missed some steps, but main logic its here, as you having the repository you can get from there whats not documented here.

Steps to reproduce

Fresh install of Lumen, and initial packages requirements its done on the following bash commands:

lumen new lumen-lighthouse-graphql
cd lumen-lighthouse-graphql
composer update
composer require nuwave/lighthouse
cp .env.example .env

setup your configuration folders, and lighthouse defaults folders needed if you are going to implement some queries, mutactions, etc…

mkdir config && mkdir app/Models && mkdir app/GraphQL && mkdir app/GraphQL/Mutations && mkdir app/GraphQL/Queries && mkdir app/GraphQL/Scalars && mkdir app/GraphQL/Directives
cp vendor/nuwave/lighthouse/config/config.php config/lighthouse.php

Helping Lumen acknowledge regarding Lighthouse:

$app->withFacades();
$app->withEloquent();
$app->configure('lighthouse');
...
$app->register(Nuwave\Lighthouse\Providers\LighthouseServiceProvider::class);

Add klaravel (Optional)

composer require ksoft/klaravel
cp vendor/ksoft/klaravel/stubs/config/ksoft.php config/ksoft.php
$app->configure('ksoft');

Laravel Passport

Im using a wrapper to be able to have Passport fully integrated on Laravel Lumen´s. For more info or extended usage:

https://github.com/dusterio/lumen-passport

composer require dusterio/lumen-passport
composer require appzcoder/lumen-routes-list
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Dusterio\LumenPassport\LumenPassport;
use Laravel\Passport\Passport;
use Carbon\Carbon;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        Passport::tokensExpireIn(Carbon::now()->addDays(15));
        Passport::refreshTokensExpireIn(Carbon::now()->addDays(30));
        LumenPassport::tokensExpireIn(Carbon::now()->addYears(50), 2);
    }
}

Under AuthServiceProvider.php add this line in the boot method

// use Dusterio\LumenPassport\LumenPassport;
LumenPassport::routes($this->app, ['prefix' => 'v1/oauth']);

User model

use Laravel\Passport\HasApiTokens;

Enable Passport under Lumen:

$app->configure('auth');
...
$app->register(Laravel\Passport\PassportServiceProvider::class);
$app->register(Dusterio\LumenPassport\PassportServiceProvider::class);
// Optional if yo want to list your routes.
if ($app->environment() == 'local') {
    $app->register(Appzcoder\LumenRoutesList\RoutesCommandServiceProvider::class);
}

Now your are going to need the migrations:

php artisan make:migration create_users_table
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->rememberToken();
$table->timestamps();

Seeder:

php artisan make:seeder UsersTableSeeder
if (!DB::table('users')->where('email', 'kiko@example.com')->first()) {
  DB::table('users')->insert([
    'name' => 'Kiko Seijo',
    'email' => 'kiko@example.com',
    'password' => app('hash')->make('secret'),
    // 'admin' => 1,
  ]);
}

Finish install Laravel Passport install with: This is the simplest way to setup a login method that Passport provides, there are many more https://laravel.com/docs/master/passport)

php artisan migrate --seed
php artisan passport:keys
php artisan passport:client --personal

Testing install:

mutation Login {
  login(username: "kiko@example.com", password: "secret") {
    user {
      id
      name
      email
    }
    token
  }
}

Should return:

{
  "data": {
    "login": {
      "user": {
        "id": "1",
        "name": "Kiko Seijo",
        "email": "kiko@example.com"
      },
      "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJS........your token"
    }
  }
}

Testing auth middleware from Viewer Query:

query ViewerQuery {
  viewer {
    id
    name
    email
  }
}

Add the headers to your query and adjust the token to the one you are getting from previous login:

{
  "Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJS........your token"
}

And you should get:

{
  "data": {
    "viewer": {
      "id": "1",
      "name": "Kiko Seijo",
      "email": "kiko@example.com"
    }
  }
}

GraphQL viewer Query with a Laravel Passport bearer token using Lighthouse auth middleware


Credits

Sunnyface.com, is a software development company from Málaga, Spain. We provide quality software based on the cloud for local & international companies, providing technology solutions with the most modern programming languages.

DevOps Web development
Custom App Development Mobile aplications
Social Apps and Startups Residents mobile application
Graphic designer Freelance senior programmer


Created by Kiko Seijo

Copias de seguridad en laravel con vistas, rutas y controladores

Todo lo que uno neceista para montar copias de seguridad en una instalación de laravel usando una librería que podemos instalar desde: laravel-activitylog

Aquí teneis todos los archivos: Descargar archivos

Lo primero es tener un controlador definido con los puntos de entrada,

BackupsControler.php

Luego creamos las vistas, las he dividido en dos por mejorar la visualización.

backup.blade.php
backup-table.blade.php

Por ultimo nos quedan las rutas de entrada, estas van en web.php,

Route::get('backup', 'BackupController@index');
Route::get('backup/create', 'BackupController@create');
Route::get('backup/download/{file_name}', 'BackupController@download');
Route::get('backup/delete/{file_name}', 'BackupController@delete');

tendremos que protegerlas por medio de un middleware, en mi caso he optado por usar el consctructor del controlador y añadido mi middleware:

public function __construct()
{
    $this->middleware('auth');
}

Tendremos que ajustar algunas librerias extras como los Flash:: y Alert::

Aquí os dejo todos los archivos en un gist.

https://gist.github.com/kikoseijo/d4ec87a121cba7bcb6231bfa046be291

Espero os sirva de ayuda.

Laravel lumen GraphQL Server for React Relay Moder

DEPRECATED

This project it’s no longer maintained in favor of v2 of nuwave/lighthouse.
You can find an example implementation for lumen on: https://github.com/kikoseijo/lumen-lighthouse-graphql

A GraphQL Server in PHP, its core its a fast micro-framework implementation (Lumen), and capable of delivering GraphQL (Relay) API responses.

React-Relay Modern, aka GraphQL v1.*

Visit repository

Introduction

On this Lumen version of a GraphQL server implementation you will be able to start coding right away a fully qualified GraphQL server with support for react-relay (Modern version).

With a Laravel heart, on his delighting micro-framework version (Lumen) and a help of a couple of other packages/plugins, we provide you with the basic server setup.

Why Lumen? and why PHP?

Why not my little Artisan? PHP limitations today for a GraphQL server are asynchronous calls, but this are problems PHP programmers been dealing with since the old days, the missing part are the GraphQL subscriptions, this can be resolved using lots of tools, like Redis or NodeJS.

PHP never been so optimized like it is today, its stable, fast and efficient. And Lumen?, because its a masterpiece, a lightweight version of Laravel, sharing same core and data structure. Perfect for building APIs.

For little Artisan like myself this is the perfect atmosphere to understand how things are achieved.

Who is this for?

People wanting to get hands dirty with GraphQL, to serve as a boilerplate to kick-off API projects, a knowledge of Laravel its recommended, at a installation level, not covered here.

Included features
  • Full CRUD example.
  • GraphQL Playground.
  • Database migrations.
  • GraphQl database schema generator and endpoint.
  • User authentication using Passport.
  • Ready to start after setup!
Client Web App

If you work with React we have published for you an application, there you can test all demo features, its a React + Relay based Web App. This is probably best way to have a full picture of what Relay its capable off, in my opinion, one of the best facebook´s open sourced contribution and React´s best friend. 💑

Visit the React Relay – WebApp client.

Composer y la version de PHP correcta en systemas Linux.

Cuando hay muchas version de php tenemos que hacer malabares para fijar la que nos hace falta, con programas como Laravel debemos que funcionen también para los usuarios por comandos cuando hay que hacer los deploy.

El problema

Tenemos instalada la ultima versión de php pero composer se queja de que no está detectando la última version de PHP.

No podemos installar paquetes porque nos dice que el php no es compatible con lo que nuestro nuevo paquete necesita como requerimiento mínimo.

Soluciones

Las soluciones son varias, pero lo mejor es no tocar demasiado en las librerías instaladas, y mucho menos en un servidor dedicado con clientes con webs de mas de 10 años, donde actualizar php no es una opción viable.

Creo que una de las soluciones mas sencillas es crear unos alias en el perfil del usuario que tiene los problemas, ya sea en el .profile, .bashrc, .bash_profile. Dependerá de tu distribución y forma de lograrte en tu servidor.

Alias

Tendrás que editar el fichero que carga tu usuario al hacer login, añade las 2 primeras líneas, si quieres puedes añadir las demás también, no van a hacer ningún daño.


alias php="/usr/bin/php71" alias composer="php /bin/composer" // ya es estamos, vamos a darle mas comandos. alias ka="php artisan" alias kdump="composer dump autoload"

Espero os ayude, siempre es bueno tener esto a mano para no andar buscando lejos.

Saludos.

Laravel Envoy for Virtualmin servers

Os dejo un enlace al gist donde mantengo una version de las funciones mas utilizadas al preparar un nuevo servidor que va a correr Laravel.

Para que funcione hay que tener instalado Laravel Envoy, y crear un archivo en la raiz del projecto, desde donde se lanzan los camandos.

ej: envoy run deploy conectar al servidor, descarga el commit mas reciente del repositorio que hayamos preconfigurado en nuestro projecto, en nuestro repositorio publico privado y va a instalar los paquetes de del composer que encuentre nuevo.

Podeis ver el archivo completo aquí:

https://gist.github.com/kikoseijo/205c49fbc66aba03bbb6d3ff456124ef

@servers(['remote' => 'xx.xx.com', 'local' => '127.0.0.1'])

@include('vendor/autoload.php')

@setup

    (new Dotenv\Dotenv(__DIR__, '.env'))->load();

    $now = date('Ymd-His');
    $branch = "origin/master";
    $username = "hes";
    $key_email = "no-reply@sunnyface.com";
    $repo_domain = "xx.xx.com";
    $repo_group = "xxx";
    $repo_name = "xx";
    $project_root = "/home/$username/$repo_name";
    $domain = $username.'.xxxxxxx.com';
    $slack = env('SLACK_ENDPOINT');
    $crons = [
        'Laravel Schedule' => [
            '* * * * *',
            "/usr/bin/php71 $project_root/artisan schedule:run >> /dev/null 2>&1"
        ]
    ];
    $alias = [
        'alias php="/usr/bin/php71"',
        'alias composer="php /bin/composer"',
        'alias ka="php artisan"',
        'alias kdump="composer dump autoload"',
    ];
    $envs = [
        'SLACK_ENDPOINT="https://hooks.slack.com/services/xxxxx/xxxxxx/xxxxxxxx"',
    ];
@endsetup


@task('php', ['on' => 'remote'])
    su -l  {{ $username }}
    php -v
@endtask

@task('deploy', ['on' => 'remote'])
    su -l  {{ $username }}
    cd ~/{{ $repo_name }}
    php artisan down --message="Upgrading system.." --retry=60
    git fetch --all
    git reset --hard {{ $branch }}
    git pull origin master
    composer update --no-dev --prefer-dist
    php artisan up
@endtask

@task('composer', ['on' => 'remote'])
    su -l  {{ $username }}
    cd ~/{{ $repo_name }}
    php artisan down --message="Upgrading system.." --retry=60
    composer install --no-dev --prefer-dist
    php artisan up
@endtask

@task('ssh', ['on' => 'remote', 'confirm' => true])
    su -l {{ $username }}
    [ -d ~/.ssh ] || echo "~/.ssh directory does not exist, lets create"
    [ -d ~/.ssh ] || mkdir ~/.ssh
    echo "Adding {{ $repo_domain }} domain to known_hosts"
    ssh-keyscan {{ $repo_domain }} >> ~/.ssh/known_hosts
    cd ~/.ssh
    echo "Deleting ssh keys..."
    rm -rf id_rsa id_rsa.pub
    ssh-keygen -t rsa -C "{{ $repo_domain }}" -b 4096  -N "" -f id_rsa
    cat  ~/.ssh/id_rsa.pub
@endtask

@story('setup')
    download
    install
@endstory

@task('download', ['on' => ['remote']])
    su -l {{ $username }}
    cd ~
    echo "Cloning {{ "git@" . $repo_domain .":". $repo_group ."/" . $repo_name . ".git" }}"
    git clone {{ "git@" . $repo_domain .":". $repo_group ."/" . $repo_name . ".git" }}
    cd ~/{{ $repo_name }}
    echo "Done download"
@endtask

@task('install', ['on' => ['remote']])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    composer install --no-dev
    [ -e .env ] || echo ".env does not exist, using default .env.example"
    [ -e .env ] || cp .env.example .env
    [ -e .env ] || echo "Generating new KEY for .env"
    [ -e .env ] || php artisan key:generate
    echo "Linking storage folder"
    php artisan storage:link
@endtask

@task('alias', ['on' => ['remote']])
    su -l {{ $username }}
    cd ~
    @foreach ($alias as $alia)
        LINE='{{$alia}}'
        FILE=.profile
        grep -qF "$LINE" "$FILE" || echo "$LINE" >> "$FILE"
    @endforeach
    alias
    echo "Done."
@endtask

@task('add_env', ['on' => ['remote']])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    @foreach ($envs as $env)
        LINE='{{$env}}'
        FILE=.env
        grep -qF "$LINE" "$FILE" || echo "$LINE" >> "$FILE"
    @endforeach
    cat .env
    echo "Done."
@endtask

@task('publish', ['on' => ['remote']])
    su -l {{ $username }}
    cd ~
    rm -rf public_html
    ln -s ~/{{ $repo_name }}/public ~/public_html
    echo "App should be now live."
@endtask

@task('cron_add', ['on' => 'remote'])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    echo "# write out current crontab"
    @foreach ($crons as $cron_name => $cron_job)
        echo "{{$cron_job[1]}}"
        croncmd="{{$cron_job[1]}}"
        cronjob="{{$cron_job[0]}} $croncmd"
        ( crontab -l | grep -v -F "$croncmd" ; echo "$cronjob" ) | crontab -
    @endforeach
    echo "# Done addCrons"
@endtask

@task('cron_remove', ['on' => 'remote'])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    echo "# remove crontabs"
    @foreach ($crons as $cron_name => $cron_job)
        croncmd="{{$cron_job[1]}}"
        ( crontab -l | grep -v -F "$cron_name" ) | crontab -
        ( crontab -l | grep -v -F "$croncmd" ) | crontab -
    @endforeach
    echo "# Done rmCrons"
@endtask

@task('apachelogswatch', ['on' => 'remote'])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    tail -f /var/log/virtualmin/{{$domain}}_error_log
@endtask

@task('laralogswatch', ['on' => 'remote'])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    tail -f storage/logs/laravel.log
@endtask

@task('catenv', ['on' => 'local'])
    cat .env
@endtask

@task('klean', ['on' => 'local'])
    php artisan clear-compiled
    php artisan migrate:refresh
    php artisan db:seed
@endtask

@task('dump', ['on' => 'local'])
    composer dump-autoload
    php artisan config:clear
    php artisan view:clear
    php artisan cache:clear
    php artisan clear-compiled
@endtask

@task('up', ['on' => ['remote']])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    php artisan up
@endtask

@task('down', ['on' => ['remote']])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    php artisan down
@endtask

@story('fm', ['on' => ['remote']])

    migrate_fresh
    migrate_seed
    passport
@endstory
@task('migrate_fresh', ['on' => 'remote'])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    php artisan migrate:refresh --force
@endtask
@task('migrate_seed', ['on' => 'remote'])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    composer update
    php artisan db:seed --force
@endtask
@task('passport', ['on' => 'remote'])
    su -l  {{ $username }}
    cd ~/{{ $repo_name }}
    php artisan passport:keys --force
    php artisan passport:install
    # php artisan vendor:publish --tag=passport-components
@endtask

@story('migrate')
    warning
    run_migration_seed
@endstory

@task('warning', ['on' => ['local']])
    echo "---------------------------------------------"
    echo "---------------------------------------------"
    echo "--------------- "ATTENTION" -----------------"
    echo "---------------------------------------------"
    echo "---------------------------------------------"
    echo "-------------  Are you sure?  ---------------"
    echo "---------------------------------------------"
    echo "---------------------------------------------"
@endtask

@task('run_migration_seed', ['on' => 'remote', 'confirm' => true])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    php artisan migrate --force
    @if ($seed)
        php artisan db:seed --force
    @endif
@endtask

@task('m:r', ['on' => 'remote'])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    php artisan migrate:rollback --force
@endtask

@task('rights', ['on' => 'remote', 'confirm' => true])
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    chmod -R 0777 public/upload app/storage
    find . -type d -exec chmod 775 {} \;
    find . -type f -exec chmod 664 {} \;
@endtask

@task('reload_php_server', ['on' => 'remote'])
    /etc/rc.d/init.d/php-fcgi-{{str_replace('.', '-', $domain)}} restart
@endtask

@task('reload_nginx', ['on' => 'remote'])
    systemctl restart nginx
@endtask

@task('optimizeInstallation', ['on' => 'remote'])
    echo 'start optimizeInstallation'
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    php artisan clear-compiled
    php artisan optimize
@endtask

@task('backupDatabase', ['on' => 'remote'])
    echo 'start backupDatabase'
    su -l {{ $username }}
    cd ~/{{ $repo_name }}
    php artisan backup:run
@endtask

Espero os ayude.

Atom plugin para desarrolladores, larvel, bootstrap, babel, react, bulma

Como programador avanzado una de las cosas que solemos tener son Snippets, los snippets sin trocitos de código fuente que solemos usar con frecuencia y por comodidad gracias a los atajos y editores de texto como Atom podemos tener nuestra librería de estos trocitos de código.
Sinceramente, cuando los usas ya no puedes vivir sin ellos, son de mucha utilidad cuando se hacen tareas repetitivas.

Los mio los comparto con el mundo entero a través de este plugin que he creado para Atom y recoge algunos de los lenguajes de programación que se trabajan en Atom. Entre ellos hay librerías de css, javascript o html, va creciendo a medida que se va haciendo necesario implementar nuevos.

Atom

Os dejo un plugin con muchos snippets para Atom.

https://atom.io/packages/bootstrap-3-snippets-for-atom

También podeis acceder al repositorio y ver como se ha hecho para tomar ideas, o utilizar lo que necesites, sin restricciones. es OS”

Github

https://github.com/kikoseijo/atom-sf-bootstrap-snippets

larvel, bootstrap, babel, react, bulma,…

Un saludo.

Actualizar Centos7 php5 a php7

En esta entrada os indico como actualizar la versión de PHP en un servidor Centos7. Sabemos que PHP7 cuatriplica la velocidad de procesamiento y reduce en un 30% la memoria que necesita, asi que después de valorarlo en algunos entornos en producción nos vemos obligados a instalarlos en todos los servidores nuevos que vamos creando.
Los comandos son sencillos, asumimos que los hacemos con root o si necesitas un sudoer puedes añadirle los comandos necesarios.

Antes de nada, para poder ejecutar el comando yum-config-manager necesitamos tener instalados la herramienta yum-utils, este grupo de herramientas del gestor de paquetes YUM de Centos que van a hacer la vida más fácil. Vamos a ello:

yum install yum-utils

Vamos a importar los repositorios de REMI y habilitarlos, simplemente tenemos que ejecutar un yum update y nuestros sistema estará de forma muy sencilla corriendo de forma nativa la version 7 de PHP.

wget -q http://rpms.remirepo.net/enterprise/remi-release-7.rpm
wget -q https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
rpm -Uvh remi-release-7.rpm epel-release-latest-7.noarch.rpm
# PARA PHP 7.0 EJECUTAR:
yum-config-manager --enable remi-php70
# PARA PHP 7.1 EJECUTAR:
yum-config-manager --enable remi-php71
yum update

Voilà, ahora si, lo verificamos y comprobamos que estamos corriendo la version de php elegida, ahora nuestro web server va a servir los WordPress’s 4 veces mas rápido y nuestro servidor puede dar servicio a muchos más clientes usando los mismos recursos.

[root@XXXX ~]# php -v
PHP 7.0.17 (cli) (built: Mar 14 2017 15:14:30) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2017 Zend Technologies

Os lo recomiendo si no lo habeis hecho todavía por miedo a los famoso upgrade.

Esto es todo amigos, 😜

Tunear PHP.ini para Centos7 y Nginx

Cada vez que creamos un nuevo Servidor Virtual nuestro sistema de creación replica el archivo que encuentra en /etc/php.ini, por eso lo que hacemos es editarla al crear el servidor y asi usaremos los mismos parámetros bases para todos los servidores virtuales que vamos añadiendo a cada Servidor Dedicado.

La seguridad es lo primero

Esconder la version de PHP en las cabeceras de las páginas servidas a nuestros usuarios maliciosos:

expose_php = Off

Output buffering – Error 502 Nginx

Deshabilitar output buffering, con esto deshabitamos el ob_flush(), esto evita errores tipo 502 en los servidores Nginx, sinceramente, es cuestión de gustos, si no hay buffer la experiencia del usuario se ve truncada a que termine de ejecutar un script hasta que muestra el resultado en pantalla, el caso más claro es cuando WordPress instala/actualiza los plugins y vamos viendo como se instala uno detrás de otro, con el buffering a Off solo veríamos el resultado final, el servidor retiene todo el contenido hasta que ha terminado todos los proceso que le require la carga de está página de WordPress, lo ideal es subir el limite por defecto que nos pone por defecto de 4096 bytes, pero si necesitamos recursos extras lo podemos subir a 16384 bytes.

output_buffering = Off
– or –
output_buffering = 16384

Tiempo de ejecución de los Script

En nuestro caso, corremos bases de datos bastante grandes, hacemos backups y sincronización de datos, a veces los 30 segundos que nos da PHP por defecto nos viene corta, por eso la subimos a 120 segundos el tiempo de espera a que se ejecute un script en una página php.

max_execution_time = 120

Otros valores

Queremos errores, nada más.

error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_WARNING & ~E_NOTICE

Mejor un error que una página en blanco que no dice nada.

display_errors = On

Estos valores dependen de vuestros usuarios, que tipo de aplicaciones utilizan.


post_max_size = 20M
upload_max_filesize = 19M