How can I add an image when creating a product in my app?

Topic summary

A developer is building a Shopify app using PHP/Laravel and successfully creates products via GraphQL mutation, but needs help adding images during product creation.

Current Implementation:

  • Uses productCreate mutation with basic product details (title, description, variants)
  • Product creation works, but image attachment functionality is missing

Solution Provided:
The mutation needs to be updated to include a $media parameter:

  • Add $media: [CreateMediaInput!] to the mutation signature
  • Pass media as a separate variable at the same level as input (not nested inside it)
  • Two required fields for images:
    • mediaContentType: Must be set to “IMAGE”
    • originalSource: The image URL/source

Key Technical Note:
This approach applies to Shopify API version 2023-07 and later. In previous API versions, images were nested inside the input object as an images array, but the structure changed with the newer API version.

Summarized with AI on November 14. AI used: claude-sonnet-4-5-20250929.

Hi!
I am creating a shopify app and in my controller I have created a function to create product and it’s working fine but I wanna send an image aswell when the product is created, I have tried multiple things but nothing is working.
I am attaching my code below of where the product is being created kindly guide me on how I can attach image

<?php

namespace App\Http\Controllers;

use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Routing\Controller as BaseController;
use Shopify\Clients\Graphql;
use App\Models\AppData;

/**
 * Controller to create Product and save product ID in database
 */
class ProductController extends BaseController
{
    private const CREATE_PRODUCTS_MUTATION = <<<'QUERY'
    mutation populateProduct($input: ProductInput!) {
        productCreate(input: $input) {
            product {
                id
            }
        }
    }
    QUERY;

    // 
     /**
     * Create Product when one click integration button is clicked 
     * @param Request $request
     */
    public function createProduct(Request $request)
    {
        
        $client = new Graphql($request->shop, $request->accessToken);

    $productTitle = "GiftCard";
    $variants = [
    [
        'price' => '0.00',
    ]
    ];
    
    $response = $client->query([
        "query" => self::CREATE_PRODUCTS_MUTATION,
        "variables" => [
            "input" => [
                "title" => $productTitle,
                "descriptionHtml" => "<p>This is an example product for gitfcard</p>",
                "variants" => $variants,
            ],
        ],
    ]);
    $responseContent = $response->getBody();
    $responseJson = json_decode($responseContent, true);

    if ($response->getStatusCode() == 200) {
        $productData = $responseJson['data']['productCreate']['product'];
        $productIdWithPrefix = $productData['id'];

        // Extract only the numeric part of the product ID
        $productId = substr($productIdWithPrefix, strrpos($productIdWithPrefix, '/') + 1);

        $saveResponse = $this->saveProductId($request, $productId);
        return response()->json(['success' => true, 'productId' => $productId], 200);
        // return response()->json(['success' => true], 200);
    } else {
        // The GraphQL query encountered errors
    
        $errors = $response->getErrors();
        // Handle the errors or log them
    
        // Return an error response
        return response()->json(['error' => 'Failed to create product', 'errors' => $response], 400);
    }
}

 // 
     /**
     * Save ProductID in our table against shop name when product is created
     * @param Request $request
     * @param $productId
     */
public function saveProductId(Request $request, $productId)
{
    $singleRow = AppData::where('store_url', $request->shop)->first();     //retireve the row which matches your shop url and apikey

    if ($singleRow) {
    // $singleRow->product_id = $request->product_id;
    $singleRow->product_id = $productId;
        $saved = $singleRow->save();
    return response()->json(['message' => 'Product Id saved successfully', 'value' => $singleRow], 201);
    } else {
    return response()->json(['message' => 'Failed to save Product ID'], 500);
    }
}
}
1 Like

This is how your query has to look like:

private const CREATE_PRODUCTS_MUTATION = <<<'QUERY'
    mutation populateProduct($input: ProductInput!, $media: [CreateMediaInput!]) {
        productCreate(input: $input, media: $media) {
            product {
                id
            }
        }
    }

And I am no php expert, but the query call should look something like this:

$response = $client->query([
        "query" => self::CREATE_PRODUCTS_MUTATION,
        "variables" => [
            "input" => [
                "title" => $productTitle,
                "descriptionHtml" => "

This is an example product for gitfcard

",
                "variants" => $variants,
            ],
            "media" => [
                "mediaContentType": "IMAGE",
                "originalSource": "

Notice that "media" is on the same level as "input". In previous API versions you had "images" inside of "input". This changed with API version 2023-07.

*mediaContentType* and *originalSource* are required. Since you want to set an image, always set *mediaContentType* to "IMAGE"
1 Like