A type-safe, zero-dependency Java client library for the PrestaShop WebService REST API.
Compatibility: Tested and validated against PrestaShop 8.x (specifically 8.0.5). PrestaShop 9 is not yet tested — the WebService API changed between major versions and may require updates to codegen and/or the HTTP layer.
- Full CRUD — create, read, update, delete any PrestaShop resource with a single method call
- Fluent filter API — chainable filters, sorting, pagination, and field selection
- Multilingual field support — first-class handling of
<language id="N">fields - Strong typing — 68 pre-generated entity classes covering all standard PrestaShop resources
- Zero runtime dependencies — uses only Java 11 built-ins (
java.net.http, DOM XML) - Structured error handling — domain-specific exceptions with field-level validation messages
- Image upload — multipart upload support for product images
- Code generator CLI — auto-generate entity classes from any live shop's schema introspection endpoint
- Modules
- Installation
- Quick Start
- Filtering & Querying
- Multilingual Fields
- Image Upload
- Error Handling
- Build & Test
- Code Generation
- Local Development Environment
- Known API Behaviours
| Module | Description |
|---|---|
prestajava-core |
Runtime library — zero external runtime dependencies (Java 11 built-ins only). |
prestajava-codegen |
Developer CLI — generates PSEntity Java classes from the PrestaShop schema introspection endpoint. Never a runtime dependency. |
Add prestajava-core to your Maven project:
<dependency>
<groupId>com.esposte</groupId>
<artifactId>prestajava-core</artifactId>
<version>0.2</version>
</dependency>Not yet on Maven Central. Until a release is published, build and install locally with
mvn installfrom the repo root, or consume via GitHub Packages athttps://maven.pkg.github.com/rfpe/prestajava.
PrestaShopClient client = PrestaShopClient.builder()
.baseUrl("http://localhost:8080")
.apiKey("YOUR_API_KEY")
.build();
// Get a product by ID
Product product = client.resource(Product.class).get(1);
System.out.println(product.getName().get(1)); // language id 1
// Create a resource
Manufacturer m = new Manufacturer();
m.setName("Acme");
m.setActive(true);
Manufacturer created = client.resource(Manufacturer.class).create(m);
// Update a resource
created.setName("Acme Corp");
client.resource(Manufacturer.class).update(created);
// Delete a resource
client.resource(Manufacturer.class).delete(created.getId());
// List all IDs for a resource type
List<Integer> ids = client.resource(Product.class).list();See prestajava-core/src/main/java/com/esposte/prestajava/sample/UsageExamples.java for a full runnable example covering products, manufacturers, orders, stock, and categories.
The fluent filter API supports field filters, sorting, pagination, and field selection:
// Active products, sorted by price descending, first 10 results
List<Product> products = client.resource(Product.class)
.filter()
.where("active", 1)
.display("full")
.sortBy("price", SortOrder.DESC)
.limit(10)
.fetch();
// Paginate: offset 20, fetch 10
List<Product> page = client.resource(Product.class)
.filter()
.display("full")
.limit(20, 10)
.fetch();
// Select specific fields only
List<Product> slim = client.resource(Product.class)
.filter()
.display("id", "reference", "price")
.fetch();Fields like name and description are indexed by language ID:
Product product = client.resource(Product.class).get(1);
// Read by language ID
String nameInEnglish = product.getName().get(1);
String nameInFrench = product.getName().get(2);
// Write multilingual values
MultiLangField name = MultiLangField.of(1, "Widget", 2, "Gadget");
product.setName(name);
client.resource(Product.class).update(product);To filter on a translatable field, use the per-language syntax:
// filter[name][1]=[My Product]
.where("name][1", "My Product")// Path is relative to /api/ — use "images/products/{productId}" to attach to a product
File imageFile = new File("/path/to/product.jpg");
String xmlResponse = client.uploadImage("images/products/22", imageFile);All exceptions extend PSException (a RuntimeException). Catch the base class or a specific subclass depending on how granular your handling needs to be.
| Exception | HTTP Status | When Thrown |
|---|---|---|
PSAuthException |
401 / 403 | API key is missing, invalid, or lacks permission for the resource or verb. |
PSNotFoundException |
404 | A get() or delete() targets an ID that does not exist. |
PSValidationException |
400 / 422 | PrestaShop rejected the payload. Call getErrors() for field-level messages. |
PSApiException |
other 4xx / 5xx | Unexpected HTTP error. Call getResponseBody() for the raw response. |
PSException |
— | Base class; also thrown for non-HTTP errors (XML parse failure, I/O error). |
try {
Product p = client.resource(Product.class).get(99999);
} catch (PSNotFoundException e) {
// resource does not exist
} catch (PSAuthException e) {
// bad or missing API key
} catch (PSValidationException e) {
e.getErrors().forEach(System.err::println);
} catch (PSException e) {
System.err.println("HTTP " + e.getStatusCode() + ": " + e.getMessage());
}Install both modules (codegen depends on core at build time):
mvn installRun tests:
mvn test # all tests
mvn test -Dtest=FilterQueryTest # single test class
mvn test -pl prestajava-core # one sub-moduleGenerate entity classes from a live shop's schema introspection endpoint:
# Single resource
java -jar prestajava-codegen/target/prestajava-codegen-0.2-jar-with-dependencies.jar \
--url http://localhost:8080 \
--key YOUR_API_KEY \
--resource products \
--package com.esposte.prestajava.entities \
--output prestajava-core/src/main/java
# All resources at once
java -jar prestajava-codegen/target/prestajava-codegen-0.2-jar-with-dependencies.jar \
--url http://localhost:8080 \
--key YOUR_API_KEY \
--all \
--package com.esposte.prestajava.entities \
--output prestajava-core/src/main/javaRe-run codegen whenever the PrestaShop schema changes (e.g. after adding a custom field via the back-office).
Java version note:
prestajava-codegenrequires Java 11+. If your defaultjavais older, point at the correct JDK explicitly:/path/to/jdk-11/bin/java -jar prestajava-codegen-0.2-jar-with-dependencies.jar ...
The commands below spin up a self-contained PrestaShop 8.0.5 + MySQL 5.7 stack using Docker. The shop installs itself on first boot (~2 minutes).
# 1. Shared network
docker network create prestashop-net
# 2. MySQL 5.7
docker run -d \
--name prestashop-mysql \
--network prestashop-net \
-e MYSQL_ROOT_PASSWORD=admin \
-p 3307:3306 \
mysql:5.7
# 3. PrestaShop 8.0
docker run -d \
--name prestashop-app \
--network prestashop-net \
-e DB_SERVER=prestashop-mysql \
-e PS_INSTALL_AUTO=1 \
-e PS_ERASE_DB=1 \
-e PS_INSTALL_DB=1 \
-e PS_DOMAIN=localhost:8080 \
-e PS_FOLDER_ADMIN=adminABACATE \
-p 8080:80 \
prestashop/prestashop:8.0| What | Value |
|---|---|
| Shop front | http://localhost:8080 |
| Admin panel | http://localhost:8080/adminABACATE |
| Admin e-mail | demo@prestashop.com |
| Admin password | prestashop_demo |
| WebService API key | WWRRRXXRJSDLSTZ3751CLC4SNABIWRU4 |
| MySQL host (from host) | localhost:3307 |
| MySQL credentials | root / admin |
The API key above is for the local dev shop only. Generate a new key in Back-office → Advanced Parameters → Webservice before using any other environment.
- Log in to the admin panel.
- Go to Advanced Parameters → Webservice.
- Set Enable PrestaShop's webservice to Yes.
- Add a key with the permissions you need (or full access for development).
# Stop
docker stop prestashop-app prestashop-mysql
# Restart (MySQL must start first)
docker start prestashop-mysql prestashop-app
# Tear down completely
docker rm -f prestashop-app prestashop-mysql
docker network rm prestashop-netThese are quirks of the PrestaShop 8.0.5 WebService API that affect this client:
%wildcard in filters — PrestaShop sanitizes%from filter values before the SQL query, sofilter[reference]=[demo%]returns no results. Use exact values or integer field filters instead.- Sort on
date_addfor orders —sort=[date_add_DESC]returns HTTP 500 on the orders endpoint. Usesort=[id_DESC]as a workaround. searchresource — Returns HTTP 400 on the blank-schema endpoint. It is not a standard CRUD resource and cannot be used with this client.- Multilingual field filters — To filter on a translatable field use the per-language syntax
filter[name][LANG_ID]=[value]. Plainfilter[name]=[value]is not supported.