In this tutorial, i’ll show you how to integrate Instagram API in Laravel App. We won’t be using any Instagram PHP wrapper library. Instead, we’ll use socialiteproviders/instagram provider to retrieve an access token from Instagram OAuth service and use it to make calls to Instagram API with guzzlehttp/guzzle client.
Getting Started
After creating a new project, run php artisan preset bootstrap to remove default VueJs scaffolding. Install Laravel Socialite, Instagram Socialite provider, and GuzzleHttp package.
1 2 3 |
composer require laravel/socialite composer require socialiteproviders/instagram composer require guzzlehttp/guzzle |
Instagram Socialite Provider Configuration
After installation, add \SocialiteProviders\Manager\ServiceProvider::class to service providers array in config/app.php file.
1 2 3 4 5 6 7 8 9 10 11 |
<?php return [ 'providers' => [ [...] // other service providers \SocialiteProviders\Manager\ServiceProvider::class, ], 'aliases' => [...], ]; |
Now edit app/Providers/EventServiceProvider and add SocialiteWasCalled event to your listen[] array in app/Providers/EventServiceProvider .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?php namespace App\Providers; use [...] class EventServiceProvider extends ServiceProvider { protected $listen = [ 'App\Events\Event' => [ 'App\Listeners\EventListener', ], \SocialiteProviders\Manager\SocialiteWasCalled::class => [ // add your listeners (aka providers) here 'SocialiteProviders\\Instagram\\InstagramExtendSocialite@handle', ], ]; public function boot(){...} } |
Now add Instagram config to config/service.php array.
1 2 3 4 5 |
'instagram' => [ 'client_id' => env('INSTAGRAM_KEY'), 'client_secret' => env('INSTAGRAM_SECRET'), 'redirect' => env('INSTAGRAM_REDIRECT_URI') ], |
Registering a new Instagram Client
Register a new Instagram client. Fill in the details. Make sure your URI must match env('INSTAGRAM_REDIRECT_URI'). User will be redirected back to this URI after authorization.
After registration, your app will show on the dashboard. Click manage and copy client id and secret. Edit .env file and add these credentials to it.
1 2 3 |
INSTAGRAM_KEY=YOUR_APP_ID INSTAGRAM_SECRET=YOUR_APP_SECRET INSTAGRAM_REDIRECT_URI=https://127.0.0.1/instagram/callback |
Generate Authentication Scaffolding
Run php artisan make:auth to generate default authentication scaffolding. We’ll authenticate users with conventional form based authentication. After authentication, we’ll ask them to authorize with Instagram OAuth. We’re doing this because Instagram doesn’t provide an email address. We’ll create a separate table for storing user’s Instagram access tokens and retrieve them with HasOne relation.
Migrations
We are creating two tables, users and instagram. Registered users will be stored on users table and their access token on instagram table along with their user_id. CreateUsersTable migration comes pre-created with laravel app. Run
php artisan make:migration CreateInstagramTable --create=instagram
to create a new migration and add these columns to it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?php class CreateInstagramTable extends Migration { public function up() { Schema::create('instagram', function (Blueprint $table) { $table->increments('id'); $table->integer('user_id')->unsigned(); $table->string('access_token'); $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); $table->timestamps(); }); } public function down(){...} } |
We’re creating user_id column and referencing it to id column on users table. We also specified an onDelete action when user record will be deleted. After adding your database credentials to .env file, run php artisan migrate to create database tables.
Models
Create an Instagram model with php artisan make:model Instagram
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Instagram extends Model { protected $table = 'instagram'; protected $fillable = [ 'user_id', 'insta_id', 'access_token', ]; } |
Modify your existing User model and add a method to retrieve an associated record from instagram table with One to One relation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?php namespace App; use Illuminate\Notifications\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable { use Notifiable; protected $fillable = [ 'name', 'email', 'password', ]; protected $hidden = [ 'password', 'remember_token', ]; public function instagram(){ return $this->hasOne(Instagram::class, 'user_id', 'id'); } } |
Now we’ll be able to verify if a user has an access token record on instagram table with Auth::user()->instagram.
Routes
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?php Auth::routes(); Route::group(['middleware' => ['auth']], function(){ Route::get('/', 'AppController@index'); Route::get('/search', 'AppController@search'); Route::get('/instagram', 'InstagramController@redirectToInstagramProvider'); Route::get('/instagram/callback', 'InstagramController@handleProviderInstagramCallback'); }); |
We’ve moved all routes to an authenticated route group. After authentication, user will be redirected to / URI. /instagram route will redirect users to Instagram OAuth service and /instagram/callback will receive user Object after authorization.
Instagram API Class
Create Classes folder under app directory. Now create an InstagramAPI class in it. We’ll write our API logic in this class and then bind it to the service container. We’ll also create a Facade and Alias to call its methods statically.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
<?php namespace App\Classes; use GuzzleHttp\Client; class InstagramAPI{ private $client; private $access_token; public function __construct() { $this->client = new Client([ 'base_uri' => 'https://api.instagram.com/v1/', ]); } public function setAccessToken($token){ $this->access_token = $token; } public function getUser(){ if($this->access_token){ $response = $this->client->request('GET', 'users/self/', [ 'query' => [ 'access_token' => $this->access_token ] ]); return json_decode($response->getBody()->getContents())->data; } return []; } public function getPosts(){ if($this->access_token){ $response = $this->client->request('GET', 'users/self/media/recent/', [ 'query' => [ 'access_token' => $this->access_token ] ]); return json_decode($response->getBody()->getContents())->data; } return []; } public function getTagPosts($tags){ if($this->access_token){ $response = $this->client->request('GET', 'tags/'.$tags.'/media/recent/', [ 'query' => [ 'access_token' => $this->access_token ] ]); return json_decode($response->getBody()->getContents())->data; } return []; } } |
In the constructor, we’re creating an instance of GuzzleHttp Client by passing a base URI to it. Then we have a setAccessToken() method that we’ll statically call from a middleware to set user’s access token. We’ll call getUser() , getPosts() and getTagPosts() method to retrieve data from API in controllers.
Service Provider
We’re creating a service provider to bind InstagramAPI class to the service container. Run php artisan make:provider InstagramServiceProvider to create a new service provider and add this code to it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?php namespace App\Providers; use App\Classes\InstagramAPI; use Illuminate\Support\ServiceProvider; class InstagramServiceProvider extends ServiceProvider { public function boot() { // } public function register() { $this->app->bind('InstagramAPI', function(){ return new InstagramAPI(); }); } } |
Now add your service provider to config/app.php providers[] array.
1 2 3 4 5 |
'providers' => [ [...] // other providers \SocialiteProviders\Manager\ServiceProvider::class, \App\Providers\InstagramServiceProvider::class, ], |
Facade
To use InstagramAPI class methods statically, we’ll create a facade for it. Create Facade folder in your app directory and create an InstagramFacade.php file under it. Add this code to it.
1 2 3 4 5 6 7 8 9 |
<?php namespace App\Facades; use Illuminate\Support\Facades\Facade; class Instagram extends Facade { protected static function getFacadeAccessor() { return 'InstagramAPI'; } } |
I’ll be using this Facade in controllers. If you want to, you can create an Alias for it. Add this line to config/app.php aliases[] array.
'Instagram' => App\Facades\Instagram::class
Middleware
We need to call setAccessToken() method to set user access token before using any of InstagramAPI methods in our controllers. Since sessions data is not available in service providers, the only way to do it is to call it in a controller’s constructor or middleware. Run php artisan make:middleware InstagramAPIMiddleware to create a new middleware and add this code to it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?php namespace App\Http\Middleware; use App\Facades\Instagram; use Closure; use Illuminate\Support\Facades\Auth; class InstagramAPIMiddleware { public function handle($request, Closure $next) { if(Auth::user()->instagram){ Instagram::setAccessToken(Auth::user()->instagram->access_token); } return $next($request); } } |
Also, add this middleware to app\Http\Kernal.php routerMiddleware[] array.
1 2 3 4 |
protected $routeMiddleware = [ [...] 'instagram' => \App\Http\Middleware\InstagramAPIMiddleware::class, ]; |
Now apply this middleware to authenticated route group in app/routes/web.php file.
1 2 3 |
Route::group(['middleware' => ['auth', 'instagram']], function(){ // routes }); |
Controllers
In our InstagramController, we’ve two methods.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<?php namespace App\Http\Controllers; use Illuminate\Support\Facades\Auth; use Laravel\Socialite\Facades\Socialite; class InstagramController extends Controller { public function redirectToInstagramProvider() { return Socialite::with('instagram')->scopes([ "public_content"])->redirect(); } public function handleProviderInstagramCallback() { $insta = Socialite::driver('instagram')->user(); $details = [ "access_token" => $insta->token ]; if(Auth::user()->instagram){ Auth::user()->instagram()->update($details); }else{ Auth::user()->instagram()->create($details); } return redirect('/'); } } |
redirectToInstagramProvider() will redirect user to OAuth server and handleProviderInstagramCallback() method receives a user object after user authorizes. We’re creating a new related instagram model record if it doesn’t exist. The new record will have an access token and user_id of the authenticated user. If a record already exists, we simply update the access token.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?php namespace App\Http\Controllers; use App\Facades\Instagram; use Illuminate\Http\Request; class AppController extends Controller { public function index() { return view('index') ->with('user', Instagram::getUser()) ->with('posts', Instagram::getPosts()); } public function search(Request $request){ return view('search') ->with('posts', Instagram::getTagPosts($request->tag)); } } |
In our AppController, we’re using Instagram Facade to retreieve user info, posts and tagged posts and then passing to our index.blade.php php view.
Views
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
@extends('layouts.app') @section('content') <div class="container app"> @if(Auth::user()->instagram) <div class="profile-header"> <div class="col-xs-12 col-sm-12 col-md-2 col-lg-2 picture"> <img src="{{$user->profile_picture}}" alt="..."> </div> <div class="col-xs-12 col-sm-12 col-md-10 col-lg-10 info"> <h1>{{$user->username}}</h1> <span> Posts({{$user->counts->media}}) . Followers({{$user->counts->followed_by}}) . Following({{$user->counts->follows}}) </span> </div> </div> <div class="posts col-xs-12 col-sm-12 col-md-8 col-lg-6"> @if($posts) @foreach($posts as $post) <div class="post"> <div class="caption"> <h4>{{$post->caption->text}}</h4> </div> @if($post->type === 'image') <div class="image"> <img src="{{$post->images->standard_resolution->url}}" alt=""> </div> @else @endif </div> @endforeach @else No Instagram posts @endif </div> @else <a href="/instagram" class="btn btn-default"> Authenticate with Instagram </a> @endif </div> @endsection |
In our index.blade.php view, We’ll render a button instead of content if the authenticated user doesn’t have a record on instagram table. If a user has already authorized Instagram access, we’ll render their profile info along with posts.
Exception Handling
To handle exceptions thrown by GuzzleHttp, update app\Exceptions\Handlers.php render() method.
1 2 3 4 5 6 7 |
public function render($request, Exception $exception) { if($exception instanceof GuzzleException){ return response('An error occurred when making request to InstagramAPI'); } return parent::render($request, $exception); } |
You can render custom response for errors raised by GuzzleHttp. You can raise your own custom errors and handle them here.
Instagram made some serious changes to their API after Facebook’s privacy issues. Many API endpoints for posting media and comments are deprecated. Read more about it here. I’ve set up an example repository for you. If you’ve any issues or errors, please comment. I’ll try to help you with it.
Hi there.
I’ve cloned your repository updated and after instagram login, when the user redirects to app i get this error “An error occurred when making request to InstagramAPI”
Any ideas? Thanks
Are you accessing API from an HTTPS Virtual Host? Make sure your redirect URI in .env file matches your app settings redirect URI.
I am too.
Are U resolved this issue?
I’ve tested it on both Linux and Windows and it seems to work without any issues. I’ve recorded a video.
The only reason I can think of why you might be getting this error is invalid or empty env variables. Try dumping
env('INSTAGRAM_KEY')
orenv('INSTAGRAM_SECRET')
from anywhere within your app to see if they are set up properly. There’s this issue where env variables sometimes return null values. Try after runningphp artisan config:clear
andphp artisan cache:clear
and see it it works.Thanks for this. I got it up and running. I think the issue cited by others might be that you’ve specified:
php artisan make:middleware InstagramMiddleware
But the class name is actually InstagramAPIMiddleware.
Hi everyone ! I have a little issue creating the middleware, I got this error:
$ php artisan make:middleware InstagramAPIMiddleware
In ProviderRepository.php line 208:
Class ‘App\Providers\InstagramSerivceProvider’ not found
I try this composer’s commands but it still not working:
composer update –no-scripts
composer dump-autoload -o
.. also when run any artisan query get that error!
Awesome guide, worked well! Thanks!
Hello !
In the provider section you call your provider “InstagramSerivceProvider” instead of “InstagramServiceProvider”.
Good guide btw !
thats right
great
Hello i got this issue after click the button authenticate with instagram
Call to undefined method SocialiteProviders\Instagram\Provider::getAccessTokenResponse()
how can i solve this?
thankyou 🙂
this is not working my computer.. 🙁
Class ‘App\Facades\Instagram’ not found
what is it..?
my code is all same blog
U can try Rename file App\Facades\InstagramFacade.php to App\Facades\Instagram.php
Good luck
‘providers’ => [
[…] // other providers
\SocialiteProviders\Manager\ServiceProvider::class,
\App\Providers\InstagramSerivceProvider::class, <——-
],
serivce is mistake right is service
Thanks for pointing that out.
Hey all, can you answer a quick question in regards to this tutorial. What happens with the application when the instagram access token expires?
Helpful tutorial. How to post a photo in instagram?
When I click ‘authenticate with Instagram’ this error occurs:
{“error_type”: “OAuthException”, “code”: 400, “error_message”: “Invalid scope field(s): public_content”}
Could I get a fix on this, thanks
Please have you solved this? I am having the same issue
I’m getting the same issue. Any fixes?
Is there a way to create posts?
Any plans to update this to use Facebook Graph API?
Hello
I downloaded your project from github and now I am getting this error whenever I click on authenticate with instagram button. Please help it’s urgent.
I got the error below. Any solution please
Oops, an error occurred.